/*****************************************************************************
   envy24control.c - Env24 chipset (ICE1712) control utility
   Copyright (C) 2000 by Jaroslav Kysela <perex@perex.cz>

   (2003/03/22) Changed to hbox/vbox layout.
   Copyright (C) 2003 by Søren Wedel Nielsen
   
   (16.12.2005)  Re-worked user interface -digital mixer display permanently
   visible; pcms split to another page; controls re-arranged and all pages
   scrollable for min window size and greater flexibility; pop-up menu enabled.
   Changes to levelmeters.c to prevent invalid redraws.
   New options added: 'w' to set initial window pixel width and 't' for tall equal mixer height style.
   Copyright (C) 2005 by Alan Horstmann

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
******************************************************************************/

#include "envy24control.h"
#include "midi.h"
#include "config.h"
#define _GNU_SOURCE
#include <getopt.h>

int input_channels, output_channels, pcm_output_channels, spdif_channels, view_spdif_playback, card_number;
int card_is_dmx6fire = FALSE, tall_equal_mixer_ht = 0;
char *profiles_file_name, *default_profile;

ice1712_eeprom_t card_eeprom;
snd_ctl_t *ctl;

GtkWidget *window;

GtkWidget *mixer_mix_drawing;
GtkWidget *mixer_clear_peaks_button;
GtkWidget *mixer_drawing[20];
GtkAdjustment *mixer_adj[20][2];
GtkWidget *mixer_vscale[20][2];
GtkWidget *mixer_mute_toggle[20][2];
GtkWidget *mixer_stereo_toggle[20];

GtkWidget *router_radio[10][12];

//GtkWidget *hw_master_clock_xtal_radio;
GtkWidget *hw_master_clock_xtal_22050;
GtkWidget *hw_master_clock_xtal_32000;
GtkWidget *hw_master_clock_xtal_44100;
GtkWidget *hw_master_clock_xtal_48000;
GtkWidget *hw_master_clock_xtal_88200;
GtkWidget *hw_master_clock_xtal_96000;
GtkWidget *hw_master_clock_spdif_radio;
GtkWidget *hw_master_clock_word_radio;
GtkWidget *hw_master_clock_status_label;
GtkWidget *hw_master_clock_actual_rate_label;

GtkWidget *hw_clock_state_label;
GtkWidget *hw_clock_state_locked;
GtkWidget *hw_clock_state_reset;

GtkWidget *hw_rate_locking_check;
GtkWidget *hw_rate_reset_check;

GtkAdjustment *hw_volume_change_adj;
GtkWidget *hw_volume_change_spin;

GtkWidget *hw_spdif_profi_nonaudio_radio;
GtkWidget *hw_spdif_profi_audio_radio;

GtkWidget *hw_profi_stream_stereo_radio;
GtkWidget *hw_profi_stream_notid_radio;

GtkWidget *hw_profi_emphasis_none_radio;
GtkWidget *hw_profi_emphasis_5015_radio;
GtkWidget *hw_profi_emphasis_ccitt_radio;
GtkWidget *hw_profi_emphasis_notid_radio;

GtkWidget *hw_consumer_copyright_on_radio;
GtkWidget *hw_consumer_copyright_off_radio;

GtkWidget *hw_consumer_copy_1st_radio;
GtkWidget *hw_consumer_copy_original_radio;

GtkWidget *hw_consumer_emphasis_none_radio;
GtkWidget *hw_consumer_emphasis_5015_radio;

GtkWidget *hw_consumer_category_dat_radio;
GtkWidget *hw_consumer_category_pcm_radio;
GtkWidget *hw_consumer_category_cd_radio;
GtkWidget *hw_consumer_category_general_radio;

GtkWidget *hw_spdif_professional_radio;
GtkWidget *hw_spdif_consumer_radio;
GtkWidget *hw_spdif_output_notebook;

GtkWidget *hw_spdif_input_coaxial_radio;
GtkWidget *hw_spdif_input_optical_radio;
GtkWidget *hw_spdif_switch_off_radio;

GtkWidget *input_interface_internal;
GtkWidget *input_interface_front_input;
GtkWidget *input_interface_rear_input;
GtkWidget *input_interface_wavetable;

GtkWidget *hw_phono_input_on_radio;
GtkWidget *hw_phono_input_off_radio;

GtkAdjustment *av_dac_volume_adj[10];
GtkAdjustment *av_adc_volume_adj[10];
GtkAdjustment *av_ipga_volume_adj[10];
GtkLabel *av_dac_volume_label[10];
GtkLabel *av_adc_volume_label[10];
GtkLabel *av_ipga_volume_label[10];
GtkWidget *av_dac_sense_radio[10][4];
GtkWidget *av_adc_sense_radio[10][4];

struct profile_button {
	GtkWidget *toggle_button;
	GtkWidget *entry;
} profiles_toggle_buttons[MAX_PROFILES];

GtkWidget *active_button = NULL;
GtkAdjustment *card_number_adj;

static guint meters_timeout;
static guint master_clock_status_timeout;
static guint internal_clock_status_timeout;
static guint rate_locking_status_timeout;
static guint rate_reset_status_timeout;


static void set_margin(GtkWidget *widget, int width)
{
	gtk_widget_set_margin_start(widget, width);
	gtk_widget_set_margin_top(widget, width);
	gtk_widget_set_margin_end(widget, width);
	gtk_widget_set_margin_bottom(widget, width);
}

static void create_mixer_frame(GtkWidget *box, int stream)
{
	GtkWidget *vbox;
	GtkWidget *vbox1;
	GtkWidget *hbox;
	GtkWidget *frame;
	GtkAdjustment *adj;
	GtkWidget *vscale;
	GtkWidget *drawing;
	GtkWidget *label;
	GtkWidget *toggle;
	char str[64], drawname[32];

	if (stream <= MAX_PCM_OUTPUT_CHANNELS) {
		sprintf(str, "PCM Out %i", stream);
	} else if (stream <= (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS)) {
		sprintf(str, "SPDIF Out %s", stream & 1 ? "L": "R");
	} else if (card_is_dmx6fire) {
		switch (stream) {
		case 11: sprintf(str, "CD In L");break;
		case 12: sprintf(str, "CD In R");break;
		case 13: sprintf(str, "Line In L");break;
		case 14: sprintf(str, "Line In R");break;
		case 15: sprintf(str, "Phono/Mic L");break;
		case 16: sprintf(str, "Phono/Mic R");break;
		case 19: sprintf(str, "Digital In L");break;
		case 20: sprintf(str, "Digital In R");break;
		default : sprintf(str, "????");break;
		}
	} else if (stream <= (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS)) {
		sprintf(str, "H/W In %i", stream - (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS));
	} else if (stream <= (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + MAX_SPDIF_CHANNELS)) {
		sprintf(str, "SPDIF In %s", stream & 1 ? "L": "R");
	} else {
		strcpy(str, "???");
	}

	frame = gtk_frame_new(str);
	gtk_widget_add_css_class(frame, "mixer-frame");
	gtk_box_append(GTK_BOX(box), frame);
	set_margin(frame, 2);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	gtk_widget_set_hexpand(vbox, FALSE);
	set_margin(vbox, 2);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
	gtk_box_append(GTK_BOX(vbox), hbox);
	gtk_widget_set_halign(hbox, GTK_ALIGN_FILL);

	adj = gtk_adjustment_new(96, 0, 96, 1, 16, 0);
	mixer_adj[stream-1][0] = adj;
	vscale = gtk_scale_new(GTK_ORIENTATION_VERTICAL, adj);
	mixer_vscale[stream-1][0] = vscale;
	gtk_scale_set_draw_value(GTK_SCALE(vscale), TRUE);
	gtk_box_append(GTK_BOX(hbox), vscale);
	gtk_widget_set_hexpand(vscale, TRUE);
	gtk_widget_set_halign(vscale, GTK_ALIGN_START);
	gtk_scale_set_value_pos(GTK_SCALE(vscale), GTK_POS_BOTTOM);
	gtk_scale_set_digits(GTK_SCALE(vscale), 0);
	g_signal_connect(adj, "value_changed",
			 G_CALLBACK(mixer_adjust),
			 (gpointer)(long)((stream << 16) + 0));

	vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_append(GTK_BOX(hbox), vbox1);
	gtk_widget_set_vexpand(vbox1, FALSE);

	drawing = envy_level_meter_new();
	mixer_drawing[stream-1] = drawing;
	sprintf(drawname, "Mixer%i", stream);
	gtk_widget_set_name(drawing, drawname);
	gtk_widget_set_size_request(drawing, 36, (60 * tall_equal_mixer_ht + 204));
	gtk_box_append(GTK_BOX(vbox1), drawing);
	gtk_widget_set_margin_top(drawing, 1);
	gtk_widget_set_margin_bottom(drawing, 1);

	label = gtk_label_new("");
	gtk_box_append(GTK_BOX(vbox1), label);
	gtk_widget_set_margin_top(label, 2);
	gtk_widget_set_margin_bottom(label, 2);

	adj = gtk_adjustment_new(96, 0, 96, 1, 16, 0);
	mixer_adj[stream-1][1] = adj;
	vscale = gtk_scale_new(GTK_ORIENTATION_VERTICAL, adj);
	mixer_vscale[stream-1][1] = vscale;
	gtk_scale_set_draw_value(GTK_SCALE(vscale), TRUE);
	gtk_box_append(GTK_BOX(hbox), vscale);
	gtk_widget_set_hexpand(vscale, TRUE);
	gtk_scale_set_value_pos(GTK_SCALE(vscale), GTK_POS_BOTTOM);
	gtk_scale_set_digits(GTK_SCALE(vscale), 0);
	g_signal_connect(adj, "value_changed",
			 G_CALLBACK(mixer_adjust),
			 (gpointer)(long)((stream << 16) + 1));
	
	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE);
	gtk_box_append(GTK_BOX(vbox), hbox);

	label = gtk_label_new("Left");
	gtk_widget_set_halign(label, GTK_ALIGN_START);
	gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
	gtk_box_append(GTK_BOX(hbox), label);
	
	label = gtk_label_new("Right");
	gtk_widget_set_halign(label, GTK_ALIGN_END);
	gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
	gtk_box_append(GTK_BOX(hbox), label);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
	gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE);
	gtk_box_append(GTK_BOX(vbox), hbox);

	toggle = gtk_toggle_button_new_with_label("Mute");
	mixer_mute_toggle[stream-1][0] = toggle;
	gtk_box_append(GTK_BOX(hbox), toggle);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), TRUE);
	g_signal_connect(toggle, "toggled",
			 G_CALLBACK(mixer_toggled_mute),
			 (gpointer)(long)((stream << 16) + 0));

	toggle = gtk_toggle_button_new_with_label("Mute");
	mixer_mute_toggle[stream-1][1] = toggle;
	gtk_box_append(GTK_BOX(hbox), toggle);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), TRUE);
	g_signal_connect(toggle, "toggled",
			 G_CALLBACK(mixer_toggled_mute),
			 (gpointer)(long)((stream << 16) + 1));

	toggle = gtk_toggle_button_new_with_label("L/R Gang");
	mixer_stereo_toggle[stream-1] = toggle;
	gtk_box_append(GTK_BOX(vbox), toggle);
	/* gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), TRUE); */
	g_signal_connect(toggle, "toggled",
			 G_CALLBACK(config_set_stereo), GINT_TO_POINTER(stream-1));
}


static void create_inputs_mixer(GtkWidget *main, GtkWidget *notebook, int page)
{
        GtkWidget *hbox;
        GtkWidget *vbox;

	GtkWidget *label;
	GtkWidget *scrolledwindow;
	GtkWidget *viewport;
	int stream;


	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);

        label = gtk_label_new("Monitor Inputs");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);

	/* build scrolling area */
	scrolledwindow = gtk_scrolled_window_new();
	gtk_box_append(GTK_BOX(hbox), scrolledwindow);
	gtk_widget_set_hexpand(scrolledwindow, TRUE);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
					GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);

	viewport = gtk_viewport_new(NULL, NULL);
	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolledwindow), viewport);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_viewport_set_child(GTK_VIEWPORT(viewport), vbox);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_append(GTK_BOX(vbox), hbox);

	for(stream = (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + 1); \
		stream <= input_channels + (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS); stream ++) {
		if (mixer_stream_is_active(stream))
			create_mixer_frame(hbox, stream);
	}
	for(stream = (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + 1); \
		stream <= spdif_channels + (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS); stream ++) {
		if (mixer_stream_is_active(stream))
			create_mixer_frame(hbox, stream);
	}
}

static void create_pcms_mixer(GtkWidget *main, GtkWidget *notebook, int page)
{
        GtkWidget *hbox;
        GtkWidget *vbox;

	GtkWidget *label;
	GtkWidget *scrolledwindow;
	GtkWidget *viewport;
	int stream;

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
        label = gtk_label_new("Monitor PCMs");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);

	/* build scrolling area */
	scrolledwindow = gtk_scrolled_window_new();
	gtk_box_append(GTK_BOX(hbox), scrolledwindow);
	gtk_widget_set_hexpand(scrolledwindow, TRUE);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), 
					GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);

	viewport = gtk_viewport_new(NULL, NULL);
	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolledwindow), viewport);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_viewport_set_child(GTK_VIEWPORT(viewport), vbox);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_append(GTK_BOX(vbox), hbox);

	for(stream = 1; stream <= pcm_output_channels; stream ++) {
		if (mixer_stream_is_active(stream))
			create_mixer_frame(hbox, stream);
	}
	for(stream = (MAX_PCM_OUTPUT_CHANNELS + 1); \
		stream <= spdif_channels + MAX_PCM_OUTPUT_CHANNELS; stream ++) {
		if (mixer_stream_is_active(stream) && view_spdif_playback)
			create_mixer_frame(hbox, stream);
	}

}

static void create_router_frame(GtkWidget *box, int stream, int pos)
{
	GtkWidget *vbox;
	GtkWidget *frame;
	GtkWidget *radiobutton;
	GtkWidget *label;
	GtkWidget *hseparator;
	GtkCheckButton *group;
	char str[64], str1[64];
	int idx;
	static char *table[10] = {
		"S/PDIF In L",
		"S/PDIF In R",
		"H/W In 1",
		"H/W In 2",
		"H/W In 3",
		"H/W In 4",
		"H/W In 5",
		"H/W In 6",
		"H/W In 7",
		"H/W In 8"
	};

	if (card_is_dmx6fire)
	{
                table[0] = "Digital In L";
                table[1] = "Digital In R";
                table[2] = "CD In L";
                table[3] = "CD In R";
                table[4] = "Line In L";
                table[5] = "Line In R";
                table[6] = "Phono/Mic L";
                table[7] = "Phono/Mic R";
	}

	if (stream <= MAX_OUTPUT_CHANNELS) {
		sprintf(str, "H/W Out %i (%s)", stream, stream & 1 ? "L" : "R");
	} else if (stream == (MAX_OUTPUT_CHANNELS + 1)) {
		if (card_is_dmx6fire) {
				strcpy(str, "Digital Out (L)");
			} else {
				strcpy(str, "S/PDIF Out (L)");
				}
	} else if (stream == (MAX_OUTPUT_CHANNELS + 2)) {
		if (card_is_dmx6fire) {
				strcpy(str, "Digital Out (R)");
			} else {
				strcpy(str, "S/PDIF Out (R)");
				}
	} else {
		strcpy(str, "???");
		}
	if ((stream == MAX_PCM_OUTPUT_CHANNELS + 1) || (stream == MAX_PCM_OUTPUT_CHANNELS + 2)) {
		sprintf(str1, "S/PDIF Out (%s)", stream & 1 ? "L" : "R");
	} else { 
		sprintf(str1, "PCM Out %i", stream);
	}

	frame = gtk_frame_new(str);
	gtk_box_append (GTK_BOX(box), frame);
	set_margin(frame, 6);


	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_set_homogeneous(GTK_BOX(vbox), TRUE);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);

	radiobutton = gtk_check_button_new_with_label(str1);
	router_radio[stream-1][0] = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(patchbay_toggled),
			 (gpointer)(long)((stream << 16) + 0));


	hseparator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
	gtk_widget_set_valign(hseparator, GTK_ALIGN_CENTER);
	gtk_box_append(GTK_BOX(vbox), hseparator);

	label = gtk_label_new("");

	/* the digital mixer can only be routed to HW1/2 or SPDIF1/2 */
	if( (stream <= 2) /* hw1/2 */ ||
	    ((stream > MAX_OUTPUT_CHANNELS) && (stream <= MAX_OUTPUT_CHANNELS + 2)) /* spdif1/2 */
	    ) {
		radiobutton = gtk_check_button_new_with_label(stream & 1 ? "Digital Mix L" : "Digital Mix R");
		router_radio[stream-1][1] = radiobutton;
		gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
		gtk_box_append(GTK_BOX(vbox), radiobutton);
		g_signal_connect(radiobutton, "toggled",
				 G_CALLBACK(patchbay_toggled),
				 (gpointer)(long)((stream << 16) + 1));
	}
	else {
	  label = gtk_label_new("");
	  gtk_box_append(GTK_BOX(vbox), label);
	}


	hseparator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
	gtk_widget_set_valign(hseparator, GTK_ALIGN_CENTER);
	gtk_box_append(GTK_BOX(vbox), hseparator);


	for(idx = 2 - spdif_channels; idx < input_channels + 2; idx++) {
		radiobutton = gtk_check_button_new_with_label(table[idx]);
		router_radio[stream-1][2+idx] = radiobutton;
		gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
		gtk_box_append(GTK_BOX(vbox), radiobutton);
		g_signal_connect(radiobutton, "toggled",
				 G_CALLBACK(patchbay_toggled), 
				 (gpointer)(long)((stream << 16) + 2 + idx));
	}
}

static void create_router(GtkWidget *main, GtkWidget *notebook, int page)
{
	GtkWidget *hbox;
	GtkWidget *label;
	GtkWidget *scrolledwindow;
	GtkWidget *viewport;
	int stream, pos;

	scrolledwindow = gtk_scrolled_window_new();
        label = gtk_label_new("Patchbay / Router");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolledwindow, label);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), 
				       GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);

	viewport = gtk_viewport_new(NULL, NULL);
	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolledwindow), viewport);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_viewport_set_child(GTK_VIEWPORT(viewport), hbox);

	pos = 0;
	for (stream = 1; stream <= output_channels; stream++) {
		if (patchbay_stream_is_active(stream))
			create_router_frame(hbox, stream, pos++);
	}
	for (stream = MAX_OUTPUT_CHANNELS + 1; stream <= MAX_OUTPUT_CHANNELS + spdif_channels; stream++) {
		if (patchbay_stream_is_active(stream))
			create_router_frame(hbox, stream, pos++);
	}
}

static void create_master_clock(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkWidget *label;
	GtkCheckButton *group;

	frame = gtk_frame_new("Master Clock");
	gtk_box_append(GTK_BOX(box), frame);
	gtk_widget_set_margin_start(frame, 4);
	gtk_widget_set_margin_end(frame, 4);


	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);


	radiobutton = gtk_check_button_new_with_label("Int 22050");
	hw_master_clock_xtal_22050 = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(internal_clock_toggled), 
			 (gpointer)"22050");


	radiobutton = gtk_check_button_new_with_label("Int 32000");
	hw_master_clock_xtal_32000 = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(internal_clock_toggled), 
			 (gpointer)"32000");


	radiobutton = gtk_check_button_new_with_label("Int 44100");
	hw_master_clock_xtal_44100 = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(internal_clock_toggled), 
			 (gpointer)"44100");


	radiobutton = gtk_check_button_new_with_label("Int 48000");
	hw_master_clock_xtal_48000 = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(internal_clock_toggled), 
			 (gpointer)"48000");


	radiobutton = gtk_check_button_new_with_label("Int 88200");
	hw_master_clock_xtal_88200 = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(internal_clock_toggled), 
			 (gpointer)"88200");


	radiobutton = gtk_check_button_new_with_label("Int 96000");
	hw_master_clock_xtal_96000 = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(internal_clock_toggled), 
			 (gpointer)"96000");



	radiobutton = gtk_check_button_new_with_label("S/PDIF In");
	hw_master_clock_spdif_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(internal_clock_toggled), 
			 (gpointer)"SPDIF");



	if (card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTA1010 &&
	    card_eeprom.subvendor != ICE1712_SUBDEVICE_DELTA1010LT)
		return;

	radiobutton = gtk_check_button_new_with_label("Word Clock");
	hw_master_clock_word_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(internal_clock_toggled), 
			 (gpointer)"WordClock");
	
        label = gtk_label_new("Locked");
        hw_master_clock_status_label = label;
        gtk_box_append(GTK_BOX(vbox), label);
}

static void create_rate_state(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *hbox;
	GtkWidget *check;

	frame = gtk_frame_new("Rate State");
	gtk_box_append(GTK_BOX(box), frame);
	gtk_widget_set_vexpand(frame, TRUE);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE);
	gtk_frame_set_child(GTK_FRAME(frame), hbox);
	set_margin(hbox, 6);

	check = gtk_check_button_new_with_label("Locked");
	hw_rate_locking_check = check;
	gtk_box_append(GTK_BOX(hbox), check);
	g_signal_connect(check, "toggled",
			 G_CALLBACK(rate_locking_toggled), 
			 (gpointer)"locked");


	check = gtk_check_button_new_with_label("Reset");
	hw_rate_reset_check = check;
	gtk_box_append(GTK_BOX(hbox), check);
	g_signal_connect(check, "toggled",
			 G_CALLBACK(rate_reset_toggled), 
			 (gpointer)"reset");

}

static void create_actual_rate(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *label;

	frame = gtk_frame_new("Actual Rate");
	gtk_box_append(GTK_BOX(box), frame);
	gtk_widget_set_vexpand(frame, TRUE);

	label = gtk_label_new("");
	hw_master_clock_actual_rate_label = label;
	gtk_frame_set_child(GTK_FRAME(frame), label);
	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
	set_margin(label, 6);
}

static void create_volume_change(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *hbox;
	GtkAdjustment *spinbutton_adj;
	GtkWidget *spinbutton;
	GtkWidget *label;

	frame = gtk_frame_new("Volume Change");
	gtk_box_append(GTK_BOX(box), frame);
	gtk_widget_set_vexpand(frame, TRUE);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), hbox);
	set_margin(hbox, 6);

	label = gtk_label_new("Rate");
	gtk_box_append(GTK_BOX(hbox), label);
	gtk_widget_set_hexpand(label, TRUE);
	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);

	spinbutton_adj = gtk_adjustment_new(16, 0, 255, 1, 10, 0);
	hw_volume_change_adj = spinbutton_adj;
	spinbutton = gtk_spin_button_new(spinbutton_adj, 1, 0);
	gtk_box_append(GTK_BOX(hbox), spinbutton);
	gtk_widget_set_hexpand(spinbutton, TRUE);
	gtk_widget_set_halign(spinbutton, GTK_ALIGN_CENTER);
	gtk_widget_set_valign(spinbutton, GTK_ALIGN_CENTER);
	gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spinbutton), TRUE);
	g_signal_connect(spinbutton_adj, "value_changed",
			 G_CALLBACK(volume_change_rate_adj), NULL);
	
}

static void create_spdif_output_settings_profi_data(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkCheckButton *group;

	frame = gtk_frame_new("Data Mode");
	gtk_box_append(GTK_BOX(box), frame);


	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);


	radiobutton = gtk_check_button_new_with_label("Non-audio");
	hw_spdif_profi_nonaudio_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_check_button_set_active(group, TRUE);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(profi_data_toggled), 
			 (gpointer)"Non-audio");

	radiobutton = gtk_check_button_new_with_label("Audio");
	hw_spdif_profi_audio_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(profi_data_toggled), 
			 (gpointer)"Audio");
}

static void create_spdif_output_settings_profi_stream(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkCheckButton *group;

	frame = gtk_frame_new("Stream");
	gtk_box_append(GTK_BOX(box), frame);


	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);

	radiobutton = gtk_check_button_new_with_label("Stereophonic");
	hw_profi_stream_stereo_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_check_button_set_active(group, TRUE);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(profi_stream_toggled), 
			 (gpointer)"Stereo");

	radiobutton = gtk_check_button_new_with_label("Not indicated");
	hw_profi_stream_notid_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(profi_stream_toggled), 
			 (gpointer)"NOTID");
}

static void create_spdif_output_settings_profi_emphasis(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkCheckButton *group;

	frame = gtk_frame_new("Emphasis");
	gtk_box_append(GTK_BOX(box), frame);


	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);


	radiobutton = gtk_check_button_new_with_label("No emphasis");
	hw_profi_emphasis_none_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_check_button_set_active(group, TRUE);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(profi_emphasis_toggled), 
			 (gpointer)"No");

	radiobutton = gtk_check_button_new_with_label("50/15us");
	hw_profi_emphasis_5015_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(profi_emphasis_toggled), 
			 (gpointer)"5015");

	radiobutton = gtk_check_button_new_with_label("CCITT J.17");
	hw_profi_emphasis_ccitt_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(profi_emphasis_toggled), 
			 (gpointer)"CCITT");

	radiobutton = gtk_check_button_new_with_label("Not indicated");
	hw_profi_emphasis_notid_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(profi_emphasis_toggled), 
			 (gpointer)"NOTID");
}

static void create_spdif_output_settings_profi(GtkWidget *notebook, int page)
{
	GtkWidget *hbox;
	GtkWidget *vbox;
	GtkWidget *label;

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
        label = gtk_label_new("Professional");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_append(GTK_BOX(hbox), vbox);
	set_margin(vbox, 6);

	create_spdif_output_settings_profi_data(vbox);
	create_spdif_output_settings_profi_stream(vbox);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_append(GTK_BOX(hbox), vbox);
	set_margin(vbox, 6);

	create_spdif_output_settings_profi_emphasis(vbox);
}

static void create_spdif_output_settings_consumer_copyright(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkCheckButton *group;

	frame = gtk_frame_new("Copyright");
	gtk_box_append(GTK_BOX(box), frame);
	
	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);


	radiobutton = gtk_check_button_new_with_label("Copyrighted");
	hw_consumer_copyright_on_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_check_button_set_active(group, TRUE);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(consumer_copyright_toggled), 
			 (gpointer)"Copyright");

	radiobutton = gtk_check_button_new_with_label("Copy permitted");
	hw_consumer_copyright_off_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(consumer_copyright_toggled),
			 (gpointer)"Permitted");
}

static void create_spdif_output_settings_consumer_copy(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkCheckButton *group;

	frame = gtk_frame_new("Copy");
	gtk_box_append(GTK_BOX(box), frame);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);

	radiobutton = gtk_check_button_new_with_label("1-st generation");
	hw_consumer_copy_1st_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_check_button_set_active(group, TRUE);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(consumer_copy_toggled), 
			 (gpointer)"1st");

	radiobutton = gtk_check_button_new_with_label("Original");
	hw_consumer_copy_original_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(consumer_copy_toggled), 
			 (gpointer)"Original");
}

static void create_spdif_output_settings_consumer_emphasis(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkCheckButton *group;

	frame = gtk_frame_new("Emphasis");
	gtk_box_append(GTK_BOX(box), frame);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);

	radiobutton = gtk_check_button_new_with_label("No emphasis");
	hw_consumer_emphasis_none_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_check_button_set_active(group, TRUE);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(consumer_emphasis_toggled), 
			 (gpointer)"No");

	radiobutton = gtk_check_button_new_with_label("50/15us");
	hw_consumer_emphasis_5015_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(consumer_emphasis_toggled), 
			 (gpointer)"5015");
}

static void create_spdif_output_settings_consumer_category(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkCheckButton *group;

	frame = gtk_frame_new("Category");
	gtk_box_append(GTK_BOX(box), frame);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);

	radiobutton = gtk_check_button_new_with_label("DAT");
	hw_consumer_category_dat_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_check_button_set_active(group, TRUE);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(consumer_category_toggled), 
			 (gpointer)"DAT");

	radiobutton = gtk_check_button_new_with_label("PCM encoder");
	hw_consumer_category_pcm_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(G_OBJECT(radiobutton), "toggled",
			 G_CALLBACK(consumer_category_toggled), 
			 (gpointer)"PCM");

	radiobutton = gtk_check_button_new_with_label("CD (ICE-908)");
	hw_consumer_category_cd_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(G_OBJECT(radiobutton), "toggled",
			 G_CALLBACK(consumer_category_toggled), 
			 (gpointer)"CD");

	radiobutton = gtk_check_button_new_with_label("General");
	hw_consumer_category_general_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(G_OBJECT(radiobutton), "toggled",
			 G_CALLBACK(consumer_category_toggled), 
			 (gpointer)"General");
}

static void create_spdif_output_settings_consumer(GtkWidget *notebook, int page)
{
	GtkWidget *vbox;
	GtkWidget *hbox;
	GtkWidget *label;

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	label = gtk_label_new("Consumer");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);
	set_margin(hbox, 6);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_append(GTK_BOX(hbox), vbox);
	set_margin(vbox, 6);

	create_spdif_output_settings_consumer_copyright(vbox);
	create_spdif_output_settings_consumer_copy(vbox);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_append(GTK_BOX(hbox), vbox);
	set_margin(vbox, 6);

	create_spdif_output_settings_consumer_emphasis(vbox);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_append(GTK_BOX(hbox), vbox);
	set_margin(vbox, 6);

	create_spdif_output_settings_consumer_category(vbox);
}

static void create_spdif_output_settings(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *hbox;
	GtkWidget *radiobutton;
	GtkWidget *notebook;
	GtkCheckButton *group;

	frame = gtk_frame_new("S/PDIF Output Settings");
	gtk_box_append(GTK_BOX(box), frame);
	gtk_widget_set_hexpand(frame, TRUE);
	set_margin(frame, 6);


	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_append(GTK_BOX(vbox), hbox);

	radiobutton = gtk_check_button_new_with_label("Professional");
	hw_spdif_professional_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_box_append(GTK_BOX(hbox), radiobutton);
	set_margin(radiobutton, 6);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(spdif_output_toggled), 
			 (gpointer)"Professional");

	radiobutton = gtk_check_button_new_with_label("Consumer");
	hw_spdif_consumer_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(hbox), radiobutton);
	set_margin(radiobutton, 6);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(spdif_output_toggled), 
			 (gpointer)"Consumer");


	notebook = gtk_notebook_new();
	hw_spdif_output_notebook = notebook;
	gtk_box_append(GTK_BOX(vbox), notebook);
	gtk_widget_set_vexpand(notebook, TRUE);


	create_spdif_output_settings_profi(notebook, 0);
 	create_spdif_output_settings_consumer(notebook, 1); 
}

static void create_spdif_input_select(GtkWidget *box)
{
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *radiobutton;
	GtkCheckButton *group;

	frame = gtk_frame_new("Digital Input");
	gtk_box_append(GTK_BOX(box), frame);
	set_margin(frame, 6);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);
	set_margin(vbox, 6);

	radiobutton = gtk_check_button_new_with_label("Coaxial");
	hw_spdif_input_coaxial_radio = radiobutton;
	group = GTK_CHECK_BUTTON(radiobutton);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(spdif_input_toggled), 
			 (gpointer)"Coaxial");

	radiobutton = gtk_check_button_new_with_label("Optical");
	hw_spdif_input_optical_radio = radiobutton;
	gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
	gtk_box_append(GTK_BOX(vbox), radiobutton);
	g_signal_connect(radiobutton, "toggled",
			 G_CALLBACK(spdif_input_toggled), 
			 (gpointer)"Optical");

	if (card_is_dmx6fire) {
		radiobutton = gtk_check_button_new_with_label("Internal CD");
		hw_spdif_switch_off_radio = radiobutton;
		gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
		gtk_box_append(GTK_BOX(vbox), radiobutton);
		g_signal_connect(radiobutton, "toggled",
				 G_CALLBACK(spdif_input_toggled),
				 (gpointer)"Off");
	}
}


static void create_phono_input(GtkWidget *box)
{
        GtkWidget *frame;
        GtkWidget *vbox;
        GtkWidget *radiobutton;
        GtkCheckButton *group;

        frame = gtk_frame_new("Phono Input Switch");
        gtk_box_append(GTK_BOX(box), frame);
        gtk_widget_set_margin_start(frame, 13);
        gtk_widget_set_margin_end(frame, 13);
        gtk_widget_set_margin_top(frame, 6);
        gtk_widget_set_margin_bottom(frame, 6);

        vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
        gtk_frame_set_child(GTK_FRAME(frame), vbox);
        set_margin(vbox, 6);

        radiobutton = gtk_check_button_new_with_label("Phono");
        hw_phono_input_on_radio = radiobutton;
        group = GTK_CHECK_BUTTON(radiobutton);
        gtk_box_append(GTK_BOX(vbox), radiobutton);
        g_signal_connect(radiobutton, "toggled",
                         G_CALLBACK(phono_input_toggled),
                         (gpointer)"Phono");

        radiobutton = gtk_check_button_new_with_label("Mic");
        hw_phono_input_off_radio = radiobutton;
        gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
        gtk_box_append(GTK_BOX(vbox), radiobutton);
        g_signal_connect(radiobutton, "toggled",
                         G_CALLBACK(phono_input_toggled),
                         (gpointer)"Mic");
}

static void create_input_interface(GtkWidget *box)
{
        GtkWidget *frame;
        GtkWidget *vbox;
        GtkWidget *radiobutton;
        GtkCheckButton *group;

        frame = gtk_frame_new("Line In Selector");
        gtk_box_append(GTK_BOX(box), frame);
        gtk_widget_set_margin_start(frame, 4);
        gtk_widget_set_margin_end(frame, 4);
        //set_margin(frame, 6);

        vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
        gtk_frame_set_child(GTK_FRAME(frame), vbox);
        set_margin(vbox, 6);

        radiobutton = gtk_check_button_new_with_label("Internal");
        input_interface_internal = radiobutton;
        group = GTK_CHECK_BUTTON(radiobutton);
        gtk_box_append(GTK_BOX(vbox), radiobutton);
        g_signal_connect(radiobutton, "toggled",
                         G_CALLBACK(analog_input_select_toggled),
                         (gpointer)"Internal");

        radiobutton = gtk_check_button_new_with_label("Front Input");
        input_interface_front_input = radiobutton;
        gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
        gtk_box_append(GTK_BOX(vbox), radiobutton);
        g_signal_connect(radiobutton, "toggled",
                         G_CALLBACK(analog_input_select_toggled),
                         (gpointer)"Front Input");

        radiobutton = gtk_check_button_new_with_label("Rear Input");
        input_interface_rear_input = radiobutton;
        gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
        gtk_box_append(GTK_BOX(vbox), radiobutton);
        g_signal_connect(radiobutton, "toggled",
                         G_CALLBACK(analog_input_select_toggled),
                         (gpointer)"Rear Input");

        radiobutton = gtk_check_button_new_with_label("Wavetable");
        input_interface_wavetable = radiobutton;
        gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
        gtk_box_append(GTK_BOX(vbox), radiobutton);
        g_signal_connect(radiobutton, "toggled",
                         G_CALLBACK(analog_input_select_toggled),
                         (gpointer)"Wave Table");
}

static void create_hardware(GtkWidget *main, GtkWidget *notebook, int page)
{
	GtkWidget *label;
	GtkWidget *hbox;
	GtkWidget *hbox1;
	GtkWidget *hbox2;
	GtkWidget *vbox;
	GtkWidget *vbox1;
	GtkWidget *scrolledwindow;
	GtkWidget *viewport;
	GtkWidget *hseparator;

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	label = gtk_label_new("Hardware Settings");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);

	/* Build scrolling area */
	scrolledwindow = gtk_scrolled_window_new();
	gtk_box_append(GTK_BOX(hbox), scrolledwindow);
	gtk_widget_set_hexpand(scrolledwindow, TRUE);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
					GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);

	viewport = gtk_viewport_new(NULL, NULL);
	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolledwindow), viewport);

	/* Outer box */
	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_viewport_set_child(GTK_VIEWPORT(viewport), hbox);

	/* Create boxes for controls */
	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_append(GTK_BOX(hbox), vbox);
	gtk_widget_set_margin_start(vbox, 6);
	gtk_widget_set_margin_end(vbox, 6);

	hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_append(GTK_BOX(vbox), hbox1);
	gtk_widget_set_vexpand(hbox1, FALSE);

	hseparator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
	gtk_box_append(GTK_BOX(vbox), hseparator);
	gtk_widget_set_margin_top(hseparator, 2);
	gtk_widget_set_margin_bottom(hseparator, 2);

	hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_append(GTK_BOX(vbox), hbox2);

	create_master_clock(hbox1);

	vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_box_append(GTK_BOX(hbox1), vbox1);
	gtk_widget_set_hexpand(vbox1, FALSE);
	gtk_widget_set_margin_start(vbox1, 20);
	gtk_widget_set_margin_end(vbox1, 20);

	create_rate_state(vbox1);
	create_actual_rate(vbox1);
	create_volume_change(vbox1);
	if (card_is_dmx6fire) {
		create_input_interface(hbox2);
		create_phono_input(hbox2);
	}
	if ((card_eeprom.subvendor == ICE1712_SUBDEVICE_DELTADIO2496) || (card_is_dmx6fire)) {
		create_spdif_input_select(hbox2);
	}
	create_spdif_output_settings(hbox);
}

static void create_about(GtkWidget *main, GtkWidget *notebook, int page)
{
	GtkWidget *label;
	GtkWidget *vbox;
	GtkWidget *hbox;
	GtkWidget *scrolledwindow;
	GtkWidget *viewport;

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
        label = gtk_label_new("About");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);

	/* build scrolling area */
	scrolledwindow = gtk_scrolled_window_new();
	gtk_box_append(GTK_BOX(hbox), scrolledwindow);
	gtk_widget_set_hexpand(scrolledwindow, TRUE);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
					GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);

	viewport = gtk_viewport_new(NULL, NULL);
	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolledwindow), viewport);


	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_viewport_set_child(GTK_VIEWPORT(viewport), vbox);
	set_margin(vbox, 6);


	/* Create text as labels */
	label = gtk_label_new("");
	gtk_box_append(GTK_BOX(vbox), label);
	gtk_widget_set_vexpand(label, TRUE);

	/* create first line */
	label = gtk_label_new("Envy24 Control Utility " VERSION);
	gtk_box_append(GTK_BOX(vbox), label);
	gtk_widget_set_margin_bottom(label, 6);

	/* create second line */
	label = gtk_label_new("A GTK Tool for Envy24 PCI Audio Chip");
	gtk_box_append(GTK_BOX(vbox), label);
	gtk_widget_set_margin_top(label, 6);
	gtk_widget_set_margin_bottom(label, 6);


	/* create third line */
	label = gtk_label_new("Copyright(c) 2000 by Jaroslav Kysela <perex@perex.cz>");
	gtk_box_append(GTK_BOX(vbox), label);
	gtk_widget_set_margin_top(label, 6);

	label = gtk_label_new("");
	gtk_box_append(GTK_BOX(vbox), label);
	gtk_widget_set_vexpand(label, TRUE);
}

static void create_analog_volume(GtkWidget *main, GtkWidget *notebook, int page)
{
	GtkWidget *label;
	GtkWidget *hbox;
	GtkWidget *vbox;
	GtkWidget *frame;
	GtkAdjustment *adj;
	GtkWidget *vscale;
	GtkWidget *radiobutton;
	GtkCheckButton *group;
	GtkWidget *scrolledwindow;
	GtkWidget *viewport;
	int i, j;
	static char* dmx6fire_inputs[6] = {
		"CD In (L)",
		"CD In (R)",
		"Line  (L)",
		"Line  (R)",
		"Phono (L)",
		"Phono (R)"
	};
	static char* dmx6fire_outputs[6] = {
		"Front (L)",
		"Front (R)",
		"Rear (L)",
		"Rear (R)",
		"Centre",
		"LFE"
	};


	scrolledwindow = gtk_scrolled_window_new();
        label = gtk_label_new("Analog Volume");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolledwindow, label);

	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), 
				       GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
	viewport = gtk_viewport_new(NULL, NULL);
	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolledwindow), viewport);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_viewport_set_child(GTK_VIEWPORT(viewport), hbox);

	/* create DAC */
	for(i = 0; i < envy_dac_volumes(); i++) {
		char name[32];
		sprintf(name, "DAC %d", i);
		frame = gtk_frame_new(name);
		//gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
		gtk_box_append(GTK_BOX(hbox), frame);
		set_margin(frame, 6);

		vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
		gtk_frame_set_child(GTK_FRAME(frame), vbox);
		set_margin(vbox, 6);

		/* Add friendly labels for DMX 6Fires */
		if(card_is_dmx6fire && (i < 6)){
			label = gtk_label_new(dmx6fire_outputs[i]);
			gtk_box_append(GTK_BOX(vbox), label);
			gtk_widget_set_margin_top(label, 6);
			gtk_widget_set_margin_bottom(label, 6);
		}

		adj = gtk_adjustment_new(0, -(envy_dac_max()), 0, 1, 16, 0);
		av_dac_volume_adj[i] = adj;
		vscale = gtk_scale_new(GTK_ORIENTATION_VERTICAL, adj);
		gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE);
		gtk_box_append(GTK_BOX(vbox), vscale);
		gtk_widget_set_vexpand(vscale, TRUE);
		gtk_widget_set_margin_top(vscale, 6);
		gtk_widget_set_margin_bottom(vscale, 6);
		gtk_scale_set_digits(GTK_SCALE(vscale), 0);
		g_signal_connect(adj, "value_changed",
				 G_CALLBACK(dac_volume_adjust), 
				 (gpointer)(long)(i));

	        label = gtk_label_new("000");
	        av_dac_volume_label[i] =(GtkLabel *)label;
		gtk_box_append(GTK_BOX(vbox), label);
		gtk_widget_set_margin_top(label, 6);
		gtk_widget_set_margin_bottom(label, 6);


		if (i >= envy_dac_senses())
			continue;
		group = NULL;
		for (j = 0; j < envy_dac_sense_items(); j++) {
			radiobutton = gtk_check_button_new_with_label(envy_dac_sense_enum_name(j));
			av_dac_sense_radio[i][j] = radiobutton;
			g_signal_connect(radiobutton, "toggled",
					 G_CALLBACK(dac_sense_toggled), 
					 (gpointer)(long)((i << 8) + j));
			gtk_box_append(GTK_BOX(vbox), radiobutton);
			if (group) {
				gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
			} else {
				group = GTK_CHECK_BUTTON(radiobutton);
			}
		}
	}

	/* create ADC */
	for (i = 0; i < envy_adc_volumes(); i++) {
		char name[32];
		sprintf(name, "ADC %d", i);
		frame = gtk_frame_new(name);
		//gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
		gtk_box_append(GTK_BOX(hbox), frame);
		set_margin(frame, 6);

		vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
		gtk_frame_set_child(GTK_FRAME(frame), vbox);
		set_margin(vbox, 6);

		/* Add friendly labels for DMX 6Fires */
		if(card_is_dmx6fire && (i < 6)){
			label = gtk_label_new(dmx6fire_inputs[i]);
			gtk_box_append(GTK_BOX(vbox), label);
			gtk_widget_set_margin_top(label, 6);
			gtk_widget_set_margin_bottom(label, 6);
		}

		adj = gtk_adjustment_new(0, -(envy_adc_max()), 0, 1, 16, 0);
		av_adc_volume_adj[i] = adj;
		vscale = gtk_scale_new(GTK_ORIENTATION_VERTICAL, adj);
		gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE);
		gtk_box_append(GTK_BOX(vbox), vscale);
		gtk_widget_set_vexpand(vscale, TRUE);
		gtk_widget_set_margin_top(vscale, 6);
		gtk_widget_set_margin_bottom(vscale, 6);
		gtk_scale_set_value_pos(GTK_SCALE(vscale), GTK_POS_BOTTOM);
		gtk_scale_set_digits(GTK_SCALE(vscale), 0);
		g_signal_connect(adj, "value_changed",
				 G_CALLBACK(adc_volume_adjust), 
				 (gpointer)(long)(i));

	        label = gtk_label_new("000");
	        av_adc_volume_label[i] =(GtkLabel *)label;
		gtk_box_append(GTK_BOX(vbox), label);
		gtk_widget_set_margin_top(label, 6);
		gtk_widget_set_margin_bottom(label, 6);

		if (i >= envy_adc_senses())
			continue;
		group = NULL;
		for (j = 0; j < envy_adc_sense_items(); j++) {
			radiobutton = gtk_check_button_new_with_label(envy_adc_sense_enum_name(j));
			av_adc_sense_radio[i][j] = radiobutton;
			g_signal_connect(radiobutton, "toggled",
					 G_CALLBACK(adc_sense_toggled), 
					 (gpointer)(long)((i << 8) + j));
			gtk_box_append(GTK_BOX(vbox), radiobutton);
			if (group) {
				gtk_check_button_set_group(GTK_CHECK_BUTTON(radiobutton), group);
			} else {
				group = GTK_CHECK_BUTTON(radiobutton);
			}
		}
	}

	/* create IPGA */
	for (i = 0; i < envy_ipga_volumes(); i++) {
		char name[32];
		sprintf(name, "IPGA %d", i);
		frame = gtk_frame_new(name);
		//gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
		gtk_box_append(GTK_BOX(hbox), frame);
		set_margin(frame, 6);

		vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
		gtk_frame_set_child(GTK_FRAME(frame), vbox);
		set_margin(vbox, 6);

		/* Add friendly labels for DMX 6Fires */
		if(card_is_dmx6fire && (i < 6)){
			label = gtk_label_new(dmx6fire_inputs[i]);
			gtk_box_append(GTK_BOX(vbox), label);
			gtk_widget_set_margin_top(label, 6);
			gtk_widget_set_margin_bottom(label, 6);
		}

		adj = gtk_adjustment_new(0, -36, 0, 1, 16, 0);
		av_ipga_volume_adj[i] = adj;
		vscale = gtk_scale_new(GTK_ORIENTATION_VERTICAL, adj);
		gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE);
		gtk_box_append(GTK_BOX(vbox), vscale);
		gtk_widget_set_vexpand(vscale, TRUE);
		gtk_widget_set_margin_top(vscale, 6);
		gtk_widget_set_margin_bottom(vscale, 6);
		gtk_scale_set_value_pos(GTK_SCALE(vscale), GTK_POS_BOTTOM);
		gtk_scale_set_digits(GTK_SCALE(vscale), 0);
		g_signal_connect(adj, "value_changed",
				 G_CALLBACK(ipga_volume_adjust), 
				 (gpointer)(long)(i));

	        label = gtk_label_new("000");
	        av_ipga_volume_label[i] = (GtkLabel *)label;
		gtk_box_append(GTK_BOX(vbox), label);
		gtk_widget_set_margin_top(label, 6);
		gtk_widget_set_margin_bottom(label, 6);
	}
}

int index_active_profile()
{
	gint index;
	gboolean found;

	found = FALSE;
	for (index = 0; index < MAX_PROFILES; index++)
	{
		if (active_button == profiles_toggle_buttons[index].toggle_button) {
			found = TRUE;
			break;
		}
	}

	if (found)
		return index;

	return NOTFOUND;
}

int delete_card_number(GtkWidget *delete_button)
{
	gint res;
	gint card_nr;
	gint index;

	if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (delete_button)))
		return EXIT_SUCCESS;

	card_nr = gtk_adjustment_get_value(card_number_adj);
	if ((card_nr < 0) || (card_nr >= MAX_CARD_NUMBERS)) {
		fprintf(stderr, "card number not in [0 ... %d]\n", MAX_CARD_NUMBERS - 1);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (delete_button), FALSE);
		return -EINVAL;
	}

	res = delete_card(card_number, profiles_file_name);
	if (res < 0) {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (delete_button), FALSE);
		return res;
	}
	if (card_nr == card_number) {
		for (index = 0; index < MAX_PROFILES; index++)
		{
			gtk_editable_set_text(GTK_EDITABLE (profiles_toggle_buttons[index].entry), get_profile_name(index + 1, card_number, profiles_file_name));
		}
	}

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (delete_button), FALSE);

	return EXIT_SUCCESS;
}

int restore_active_profile(const gint profile_number)
{
	gint res;

	res = save_restore(ALSACTL_OP_RESTORE, profile_number, card_number, profiles_file_name, NULL);

	return res;
}

int save_active_profile(GtkWidget *save_button)
{
	gint res;
	gint index;

	if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (save_button)))
		return EXIT_SUCCESS;
	if ((index = index_active_profile()) >= 0) {
		res = save_restore(ALSACTL_OP_STORE, index + 1, card_number, profiles_file_name, \
			gtk_editable_get_text(GTK_EDITABLE (profiles_toggle_buttons[index].entry)));
	} else {
		fprintf(stderr, "No active profile found.\n");
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (save_button), FALSE);
		return -EXIT_FAILURE;
	}

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (save_button), FALSE);

	return res;
}

void entry_toggle_editable(GtkWidget *toggle_button, GtkWidget *entry)
{
	gint index;
	gint profile_number;

	if (active_button == toggle_button) {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (toggle_button), TRUE);
		gtk_editable_set_editable(GTK_EDITABLE (entry), TRUE);
		gtk_widget_grab_focus(entry);
		return;
	} else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle_button))) {
		active_button = toggle_button;
	}
	gtk_editable_set_editable(GTK_EDITABLE (entry), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle_button)));
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle_button))) {
		gtk_widget_grab_focus(entry);
		profile_number = NOTFOUND;
		for (index = 0; index < MAX_PROFILES; index++)
		{
			if (profiles_toggle_buttons[index].toggle_button != toggle_button) {
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (profiles_toggle_buttons[index].toggle_button), FALSE);
			} else {
				profile_number = index + 1;
			}
		}
		if (profile_number >= 0)
			restore_active_profile(profile_number);
	}
}

void enter_callback( const GtkWidget *widget, const GtkWidget *entry )
{
	const gchar *entry_text;
	entry_text = gtk_editable_get_text (GTK_EDITABLE (entry));
	printf("Inhalt : %s\n", entry_text);
}

static void toggle_button_entry_destroy(GtkWidget *self, gpointer data)
{
	GtkWidget *entry = (GtkWidget*)data;
	gtk_widget_unparent(entry);
}

static GtkWidget *toggle_button_entry(const GtkWidget *parent, const gchar *profile_name, const gint index)
{
	GtkWidget *entry_label;
	GtkWidget *toggle_button;

	toggle_button = gtk_toggle_button_new();
	set_margin(toggle_button, 3);

	profiles_toggle_buttons[index].entry = entry_label = gtk_entry_new();
	gtk_entry_set_max_length(GTK_ENTRY (entry_label), MAX_PROFILE_NAME_LENGTH);
	gtk_editable_set_width_chars(GTK_EDITABLE (entry_label), 16);
	gtk_editable_set_text(GTK_EDITABLE (entry_label), profile_name);
	/* only the active profile can be modified */
	gtk_editable_set_editable(GTK_EDITABLE (entry_label), FALSE);
	g_signal_connect(entry_label, "activate",
			 G_CALLBACK (enter_callback),
			 (gpointer) entry_label);
	g_signal_connect(toggle_button, "toggled",
			 G_CALLBACK (entry_toggle_editable),
			 (gpointer) entry_label);
	g_signal_connect(toggle_button, "destroy",
			 G_CALLBACK (toggle_button_entry_destroy),
			 (gpointer) entry_label);

	gtk_widget_set_parent(entry_label, toggle_button);
	gtk_widget_set_margin_start(entry_label, 20);
	gtk_widget_set_margin_end(entry_label, 20);
	return (toggle_button);
}

static void create_profiles(GtkWidget *main, GtkWidget *notebook, int page)
{
	GtkWidget *label;
	GtkWidget *label_card_nr;
	GtkWidget *vbox1;
	GtkWidget *vbox2;
	GtkWidget *hbox;
	GtkWidget *hbox1;
	GtkWidget *save_button;
	GtkWidget *delete_button;
	GtkAdjustment *card_button_adj;
	GtkWidget *card_button;
	GtkWidget *scrolledwindow;
	GtkWidget *viewport;
	gint index;
	gint profile_number;
	gchar *profile_name;
	gint max_profiles;
	gint max_digits;

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
        label = gtk_label_new("Profiles");
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);

	/* build scrolling area */
	scrolledwindow = gtk_scrolled_window_new();
	gtk_box_append(GTK_BOX(hbox), scrolledwindow);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
					GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);

	viewport = gtk_viewport_new(NULL, NULL);
	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolledwindow), viewport);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_viewport_set_child(GTK_VIEWPORT(viewport), hbox);


	/* Create button boxes */
	vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);

	for (index = 0; index < MAX_PROFILES; index++)	{
		profile_name = get_profile_name(index + 1, card_number, profiles_file_name);
		profiles_toggle_buttons[index].toggle_button = toggle_button_entry(window, profile_name, index);
		gtk_box_append(GTK_BOX (vbox1), profiles_toggle_buttons[index].toggle_button);
		gtk_widget_set_vexpand(profiles_toggle_buttons[index].toggle_button, index != 0 ? TRUE : FALSE);
		gtk_widget_set_valign(profiles_toggle_buttons[index].toggle_button, GTK_ALIGN_END);
		gtk_widget_set_halign(profiles_toggle_buttons[index].toggle_button, GTK_ALIGN_CENTER);
	}
	set_margin(vbox1, 6);

	vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	set_margin(vbox2, 50);

	hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_append(GTK_BOX(vbox2), hbox1);
	gtk_widget_set_halign(hbox1, GTK_ALIGN_CENTER);

        label_card_nr = gtk_label_new("Card Number:");
	gtk_box_append(GTK_BOX(hbox1), label_card_nr);
	gtk_label_set_justify(GTK_LABEL(label_card_nr), GTK_JUSTIFY_LEFT);
	gtk_widget_set_margin_start(label_card_nr, 20);
	gtk_widget_set_margin_end(label_card_nr, 20);

	card_button_adj = gtk_adjustment_new(16, 0, MAX_CARD_NUMBERS - 1, 1, 10, 0);
	card_number_adj = card_button_adj;
	card_button = gtk_spin_button_new(card_button_adj, 1, 0);
	gtk_box_append(GTK_BOX (hbox1), card_button);
	gtk_widget_set_hexpand(card_button, TRUE);
	gtk_widget_set_halign(card_button, GTK_ALIGN_CENTER);
	gtk_spin_button_set_numeric(GTK_SPIN_BUTTON (card_button), TRUE);
	gtk_adjustment_set_value(card_button_adj, card_number);

	delete_button = gtk_toggle_button_new_with_label("Delete card from profiles");
	gtk_box_append(GTK_BOX (vbox2), delete_button);
	gtk_widget_set_vexpand(delete_button, TRUE);
	gtk_widget_set_valign(delete_button, GTK_ALIGN_END);
	g_signal_connect(delete_button, "toggled",
			 G_CALLBACK (delete_card_number),
			 NULL);

	save_button = gtk_toggle_button_new_with_label("Save active profile");
	gtk_box_append(GTK_BOX (vbox2), save_button);
	gtk_widget_set_vexpand(save_button, TRUE);
	gtk_widget_set_valign(save_button, GTK_ALIGN_END);
	g_signal_connect(save_button, "toggled",
			 G_CALLBACK (save_active_profile),
			 NULL);

	gtk_box_append(GTK_BOX(hbox), vbox1);
	gtk_widget_set_hexpand(vbox1, TRUE);
	gtk_box_append(GTK_BOX(hbox), vbox2);
	gtk_widget_set_halign(vbox2, GTK_ALIGN_CENTER);

	if (default_profile != NULL)
	{
		/*
		 * only if default_profile is numerical and lower or equal than MAX_PROFILES it will be a profile_number
		 * otherwise it will be a profile name
		 */
		if (strcspn(default_profile, "0123456789") == 0) {
			for (max_profiles = MAX_PROFILES, max_digits = 0; max_profiles > 9; max_digits++, max_profiles /= 10)
				;
			max_digits++;
			if (strlen(default_profile) <= max_digits) {
				profile_number = atoi(default_profile);
				if (profile_number < 1 || profile_number > MAX_PROFILES)
					profile_number = get_profile_number(default_profile, card_number, profiles_file_name);
			} else {
				profile_number = get_profile_number(default_profile, card_number, profiles_file_name);
			}
		} else {
			profile_number = get_profile_number(default_profile, card_number, profiles_file_name);
		}
		if ((profile_number > 0) && (profile_number <= MAX_PROFILES)) {
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (profiles_toggle_buttons[profile_number - 1].toggle_button), TRUE);
		} else {
			fprintf(stderr, "Cannot find profile '%s' for card '%d'.\n", default_profile, card_number);
		}
	}
}

static void create_outer(GtkWidget *main)
{
        GtkWidget *hbox1;
        GtkWidget *vbox;

	GtkWidget *label;
	GtkWidget *frame;
	GtkWidget *drawing;

	/* Create digital mixer frame */
	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
	gtk_box_append(GTK_BOX(main), vbox);

	label = gtk_label_new(" Rt-clk Menu >>");
	//gtk_widget_set_halign(label, GTK_ALIGN_START);
	//gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
	gtk_box_append(GTK_BOX(vbox), label);
	gtk_widget_set_margin_top(label, 3);
	gtk_widget_set_margin_bottom(label, 3);
	frame = gtk_frame_new("Digital Mixer");
	gtk_widget_add_css_class(frame, "mixer-frame");
	gtk_box_append(GTK_BOX(vbox), frame);
	set_margin(frame, 6);

	/* Create controls in the digital mixer frame */

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_frame_set_child(GTK_FRAME(frame), vbox);

	hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_append(GTK_BOX(vbox), hbox1);
	gtk_widget_set_halign(hbox1, GTK_ALIGN_CENTER);
	gtk_widget_set_margin_top(hbox1, 6);
	gtk_widget_set_margin_bottom(hbox1, 6);

	drawing = envy_level_meter_new();
	mixer_mix_drawing = drawing;
	gtk_widget_set_name(drawing, "DigitalMixer");
	gtk_box_append(GTK_BOX(hbox1), drawing);
	if (tall_equal_mixer_ht > 1 ) {
		gtk_widget_set_size_request(drawing, 60, 264 + 60 * (tall_equal_mixer_ht - 1));
	} else {
		gtk_widget_set_size_request(drawing, 60, 264);
	}

	hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	gtk_box_set_homogeneous(GTK_BOX(hbox1), TRUE);
	gtk_box_append(GTK_BOX(vbox), hbox1);
	set_margin(hbox1, 6);

	label = gtk_label_new("Left");
	gtk_widget_set_halign(label, GTK_ALIGN_START);
	gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
	gtk_box_append(GTK_BOX(hbox1), label);

	label = gtk_label_new("Right");
	gtk_widget_set_halign(label, GTK_ALIGN_END);
	gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
	gtk_box_append(GTK_BOX(hbox1), label);


	mixer_clear_peaks_button = gtk_button_new_with_label("Reset Peaks");
	gtk_box_append(GTK_BOX(vbox), mixer_clear_peaks_button);
	set_margin(mixer_clear_peaks_button, 4);
	g_signal_connect(mixer_clear_peaks_button, "clicked",
			 G_CALLBACK(level_meters_reset_peaks), NULL);
}/* End create_outer  */

static void close_window(void)
{
	g_source_remove(meters_timeout);
	g_source_remove(master_clock_status_timeout);
	g_source_remove(internal_clock_status_timeout);
	g_source_remove(rate_locking_status_timeout);
	g_source_remove(rate_reset_status_timeout);
}

static void usage(void)
{
	fprintf(stderr, "usage: envy24control [-c card#] [-D control-name] [-o num-outputs] [-i num-inputs] [-p num-pcm-outputs] [-s num-spdif-in/outs] [-v] [-f profiles-file] [profile name|profile id] [-m channel-num] [-w initial-window-width] [-t height-num]\n");
	fprintf(stderr, "\t-c, --card\tAlsa card number to control\n");
	fprintf(stderr, "\t-D, --device\tcontrol-name\n");
	fprintf(stderr, "\t-o, --outputs\tLimit number of analog line outputs to display\n");
	fprintf(stderr, "\t-i, --input\tLimit number of analog line inputs to display\n");
	fprintf(stderr, "\t-p, --pcm_output\tLimit number of PCM outputs to display\n");
	fprintf(stderr, "\t-s, --spdif\tLimit number of spdif inputs/outputs to display\n");
	fprintf(stderr, "\t-v, --view_spdif_playback\tshows the spdif playback channels in the mixer\n");
	fprintf(stderr, "\t-f, --profiles_file\tuse file as profiles file\n");
	fprintf(stderr, "\t-m, --midichannel\tmidi channel number for controller control\n");
	fprintf(stderr, "\t-M, --midienhanced\tUse an enhanced mapping from midi controller to db slider\n");
	fprintf(stderr, "\t-w, --window_width\tSet initial window width (try 2,6 or 8; 280,626, or 968)\n");
	fprintf(stderr, "\t-t, --tall_eq_mixer_heights\tSet taller height mixer displays (1-9)\n");
}

int main(int argc, char **argv)
{
        GtkWidget *notebook;
	GtkWidget *outerbox;
	GtkCssProvider *provider;
        char *name, tmpname[8], title[128];
	int i, c, err;
	snd_ctl_card_info_t *hw_info;
	snd_ctl_elem_value_t *val;
	int npfds;
	struct pollfd *pfds;
	int midi_fd = -1, midi_channel = -1, midi_enhanced = 0;
	int page;
	int input_channels_set = 0;
	int output_channels_set = 0;
	int pcm_output_channels_set = 0;
	int width_val;
	int wwidth_set =FALSE;
	int wwidth = 796;
	const int chanwidth = 86;
	const int fixwidth = 108;
	const char *css;

	static struct option long_options[] = {
		{"device", 1, 0, 'D'},
		{"card", 1, 0, 'c'},
		{"profiles_file", 1, 0, 'f'},
		{"inputs", 1, 0, 'i'},
		{"midichannel", 1, 0, 'm'},
		{"midienhanced", 0, 0, 'M'},
		{"outputs", 1, 0, 'o'},
		{"pcm_outputs", 1, 0, 'p'},
		{"spdif", 1, 0, 's'},
		{"window_width", 1, 0, 'w'},
		{"view_spdif_playback", 0, 0, 'v'},
		{"tall_eq_mixer_heights", 1, 0, 't'},
		{ NULL }
	};


	snd_ctl_card_info_alloca(&hw_info);
	snd_ctl_elem_value_alloca(&val);

	/* Go through gtk initialization */
        gtk_init();
	g_set_prgname("envy24control");

	name = NULL; /* probe */
	card_number = 0;
	input_channels = MAX_INPUT_CHANNELS;
	output_channels = MAX_OUTPUT_CHANNELS;
	pcm_output_channels = MAX_PCM_OUTPUT_CHANNELS;
	spdif_channels = MAX_SPDIF_CHANNELS;
	view_spdif_playback = 0;
	profiles_file_name = DEFAULT_PROFILERC;
	default_profile = NULL;
	while ((c = getopt_long(argc, argv, "D:c:f:i:m:Mo:p:s:w:vt:", long_options, NULL)) != -1) {
		switch (c) {
		case 'D':
			name = optarg;
			card_number = atoi(strchr(name, ':') + sizeof(char));
			if (card_number < 0 || card_number >= MAX_CARD_NUMBERS) {
				fprintf(stderr, "envy24control: invalid card number %d\n", card_number);
				exit(1);
			}
			break;
		case 'c':
			i = atoi(optarg);
			if (i < 0 || i >= MAX_CARD_NUMBERS) {
				fprintf(stderr, "envy24control: invalid card number %d\n", i);
				exit(1);
			}
			card_number = i;
			sprintf(tmpname, "hw:%d", i);
			name = tmpname;
			break;
		case 'f':
			profiles_file_name = optarg;
			break;
		case 'i':
			input_channels = atoi(optarg);
			if (input_channels < 0 || input_channels > MAX_INPUT_CHANNELS) {
				fprintf(stderr, "envy24control: must have 0-%i inputs\n", MAX_INPUT_CHANNELS);
				exit(1);
			}
			input_channels_set = 1;
			break;
		case 'm':
			midi_channel = atoi(optarg);
			if (midi_channel < 1 || midi_channel > 16) {
				fprintf(stderr, "envy24control: invalid midi channel number %i\n", midi_channel);
				exit(1);
			}
			--midi_channel;
			break;
		case 'M': midi_enhanced = 1; break;
		case 'o':
			output_channels = atoi(optarg);
			if (output_channels < 0 || output_channels > MAX_OUTPUT_CHANNELS) {
				fprintf(stderr, "envy24control: must have 0-%i outputs\n", MAX_OUTPUT_CHANNELS);
				exit(1);
			}
			output_channels_set = 1;
			break;
		case 'p':
			pcm_output_channels = atoi(optarg);
			if (pcm_output_channels < 0 || pcm_output_channels > MAX_PCM_OUTPUT_CHANNELS) {
				fprintf(stderr, "envy24control: must have 0-%i pcm outputs\n", MAX_PCM_OUTPUT_CHANNELS);
				exit(1);
			}
			pcm_output_channels_set = 1;
			break;
		case 's':
			spdif_channels = atoi(optarg);
			if (spdif_channels < 0 || spdif_channels > MAX_SPDIF_CHANNELS) {
				fprintf(stderr, "envy24control: must have 0-%i spdifs\n", MAX_SPDIF_CHANNELS);
				exit(1);
			}
			break;
		case 'w':
			width_val = atoi(optarg);
			if ((width_val >= 1) && (width_val <= 20)) {
				wwidth = (width_val * chanwidth + fixwidth);
			} else {
				wwidth = width_val;
			}
			wwidth_set = TRUE;
			break;
		case 'v':
			view_spdif_playback = 1;
			break;
		case 't':
			tall_equal_mixer_ht = atoi(optarg);
			if ((tall_equal_mixer_ht < 0) || (tall_equal_mixer_ht >= 10))
				tall_equal_mixer_ht = 0;
			break;
		default:
			usage();
			exit(1);
			break;
		}
	}
	if (optind < argc) {
		default_profile = argv[optind];
	}

	if (! name) {
		/* probe cards */
		static char cardname[16];
		/* FIXME: hardcoded max number of cards */
		for (card_number = 0; card_number < 8; card_number++) {
			sprintf(cardname, "hw:%d", card_number);
			if (snd_ctl_open(&ctl, cardname, 0) < 0)
				continue;
			if (snd_ctl_card_info(ctl, hw_info) < 0 ||
			    strcmp(snd_ctl_card_info_get_driver(hw_info), "ICE1712")) {
				snd_ctl_close(ctl);
				continue;
			}
			/* found */
			name = cardname;
			break;
		}
		if (! name) {
			fprintf(stderr, "No ICE1712 cards found\n");
			exit(EXIT_FAILURE);
		}
	} else {
		if ((err = snd_ctl_open(&ctl, name, 0)) < 0) {
			fprintf(stderr, "snd_ctl_open: %s\n", snd_strerror(err));
			exit(EXIT_FAILURE);
		}
		if ((err = snd_ctl_card_info(ctl, hw_info)) < 0) {
			fprintf(stderr, "snd_ctl_card_info: %s\n", snd_strerror(err));
			exit(EXIT_FAILURE);
		}
		if (strcmp(snd_ctl_card_info_get_driver(hw_info), "ICE1712")) {
			fprintf(stderr, "invalid card type (driver is %s)\n", snd_ctl_card_info_get_driver(hw_info));
			exit(EXIT_FAILURE);
		}
	}

	snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_CARD);
	snd_ctl_elem_value_set_name(val, "ICE1712 EEPROM");
	if ((err = snd_ctl_elem_read(ctl, val)) < 0) {
		fprintf(stderr, "Unable to read EEPROM contents: %s\n", snd_strerror(err));
		exit(EXIT_FAILURE);
	}
	memcpy(&card_eeprom, snd_ctl_elem_value_get_bytes(val), 32);

	if(card_eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE)
		card_is_dmx6fire = TRUE;

	/* Set a better default for input_channels and output_channels */
	if(!input_channels_set)
		if(card_is_dmx6fire)
			input_channels = 6;

	if(!output_channels_set)
		if(card_is_dmx6fire)
			output_channels = 6;

	if(!pcm_output_channels_set)
		if(card_is_dmx6fire)
			pcm_output_channels = 6; /* PCMs 7&8 can be used -set using option -p8 */

	if (!wwidth_set)
		if (card_is_dmx6fire)
			wwidth = 626;


	/* Initialize code */
	config_open();
	level_meters_init();
	mixer_init();
	patchbay_init();
	hardware_init();
	analog_volume_init();
	if (midi_channel >= 0)
		midi_fd = midi_init(argv[0], midi_channel, midi_enhanced);

	fprintf(stderr, "using\t --- input_channels: %i\n\t --- output_channels: %i\n\t --- pcm_output_channels: %i\n\t --- spdif in/out channels: %i\n", \
		input_channels, output_channels, pcm_output_channels, spdif_channels);

	/* Reduce padding so the mixers don't get so wide */
	provider = gtk_css_provider_new();
	css = ".mixer-frame button { padding-left: 6px; padding-right: 6px; }\n"
		".mixer-frame scale { padding-left: 8px; padding-right: 8px; }";
#if GTK_CHECK_VERSION(4, 12, 0)
	gtk_css_provider_load_from_string(provider, css);
#else
	gtk_css_provider_load_from_data(provider, css, -1);
#endif
	gtk_style_context_add_provider_for_display(gdk_display_get_default(),
						   GTK_STYLE_PROVIDER(provider),
						   GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
	g_object_unref(provider);

        /* Make the title */
        sprintf(title, "Envy24 Control Utility %s (%s)", VERSION, snd_ctl_card_info_get_longname(hw_info));

        /* Create the main window */
        window = gtk_window_new();
        gtk_window_set_title(GTK_WINDOW(window), title);
        g_signal_connect(window, "destroy", G_CALLBACK(close_window), NULL);

	gtk_window_set_default_size(GTK_WINDOW(window), wwidth, 300);

	outerbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
	gtk_window_set_child(GTK_WINDOW(window), outerbox);

	create_outer(outerbox);

        /* Create the notebook */
        notebook = gtk_notebook_new();
	gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
	gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
	gtk_box_append(GTK_BOX(outerbox), notebook);

	page = 0;

	create_inputs_mixer(outerbox, notebook, page++);
	create_pcms_mixer(outerbox, notebook, page++);
	create_router(outerbox, notebook, page++);
	create_hardware(outerbox, notebook, page++);
	if (envy_analog_volume_available())
		create_analog_volume(outerbox, notebook, page++);
	create_profiles(outerbox, notebook, page++);
	create_about(outerbox, notebook, page++);

	npfds = snd_ctl_poll_descriptors_count(ctl);
	if (npfds > 0) {
		pfds = alloca(sizeof(*pfds) * npfds);
		npfds = snd_ctl_poll_descriptors(ctl, pfds, npfds);
		for (i = 0; i < npfds; i++) {
			GIOChannel *channel = g_io_channel_unix_new(pfds[i].fd);
			g_io_add_watch(channel,
				       G_IO_IN,
				       control_input_callback,
				       ctl);
			g_io_channel_unref(channel);
		}
		snd_ctl_subscribe_events(ctl, 1);
	}
	if (midi_fd >= 0) {
		GIOChannel *channel = g_io_channel_unix_new(midi_fd);
		g_io_add_watch(channel, G_IO_IN, midi_process, NULL);
		g_io_channel_unref(channel);
	}
	meters_timeout = g_timeout_add(40, level_meters_timeout_callback, NULL);
	master_clock_status_timeout = g_timeout_add(100, master_clock_status_timeout_callback, NULL);
	internal_clock_status_timeout = g_timeout_add(100, internal_clock_status_timeout_callback, NULL);
	rate_locking_status_timeout = g_timeout_add(100, rate_locking_status_timeout_callback, NULL);
	rate_reset_status_timeout = g_timeout_add(100, rate_reset_status_timeout_callback, NULL);


	gtk_window_present(GTK_WINDOW(window));

	level_meters_postinit();
	mixer_postinit();
	patchbay_postinit();	
	hardware_postinit();
	analog_volume_postinit();

	while (g_list_model_get_n_items(gtk_window_get_toplevels()) > 0)
		g_main_context_iteration(NULL, TRUE);

	snd_ctl_close(ctl);
	midi_close();
	config_close();

	return EXIT_SUCCESS;
}
