diff --git a/envy24control/AUTHORS b/envy24control/AUTHORS index 7e8d506..a46570e 100644 --- a/envy24control/AUTHORS +++ b/envy24control/AUTHORS @@ -1 +1,28 @@ Jaroslav Kysela + + volume.c: added by Takashi Iwai + + (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 + + (July 2010) NPM: (1) Implemented "Peak Hold" functionality in meters; + reimplemented meters to do away with inefficient "faux LED" peak-meter + display, which causes thousands of rectangles per-second to be drawn, + individually, for no good reason other than increasing the load. (2) + Significantly reduced the number of timer interrupts generated by this + program by slowing down all updates to 10 per second -- previously + meters updated 25x/second! (3) All volumes are represented as decibels, + including the 0 to -48dB range of the hardware peak-meters, the 0 -to- + -144dB attenuation for all inputs to the digital mixer, the 0 -to- -63dB + attenuation of the analog DAC, and the +18 -to- -63dB + attenuation/amplification of the analog ADC. (4) All gtk "scale" widgets + have dB legends; the "PageUp" "PageDown" keys allow rapid movement + between the marked levels. + Copyright (C) 2010 Niels Mayer ( http://nielsmayer.com ) diff --git a/envy24control/README b/envy24control/README index 40c4fe6..550bbc2 100644 --- a/envy24control/README +++ b/envy24control/README @@ -14,3 +14,39 @@ To build (from GIT): ./gitcompile su -c 'make install' +-------------------- + +The "Monitor Inputs" and "Monitor PCMs" tabs contain multiple scale widgets +grouped into L/R pairs and an associated peak-level meter. Each scale +widget represents the 24 bit attenuation value of each input to the +ice1712-based soundcard's digital mixer. This mixer is typically used for +zero-latency monitoring of "live" inputs, alongside backing sounds and +effects coming from the eight channels of PCM feeding the digital mixer. +When many inputs are "hot" simultaneously these scale-widgets attenuate the +inputs going into the digital mixer to prevent the output from clipping. +For details see http://nielsmayer.com/npm/envy24mixer-architecture.png + +(from http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf ) + +This is what the above manual says about the Envy24's digital mixer: +> 4.5.5 Multi-Track Digital Monitoring +> +> The Envy24 integrates a 36-bit resolution digital hardware mixer. The +> width of the data path is strictly to ensure that during processing of +> all the channels, under any condition, no resolution is lost. The +> dynamic range of the end user system will be limited by the range of the +> physical output devices used. In order to maintain identical gain to the +> input stream (i.e. 0dB), the resulting 24-bit is not msb-aligned to the +> 36-bit. The overflow bits correspond to the analog distortion due to +> saturation. The user would need to reduce the overall attenuation of the +> inputs to avoid clipping. Insertion of the digital mixer adds only a +> single sample cycle delay with respect to the original data. This +> extremely low latency all digital mixer provides monitoring +> functionality and can replace a traditional external analog input +> mixer. There are 20 independent audio data streams to mix and control +> the volume. + +And here's what it says about the Envy24's hardware peak metering: + +> Peak data derived from the absolute value of 9 msb. 00h min - FFh max +> volume. Reading the register resets the meter to 00h. diff --git a/envy24control/config.c b/envy24control/config.c index 6933eef..828d8bc 100644 --- a/envy24control/config.c +++ b/envy24control/config.c @@ -46,7 +46,7 @@ void config_close() void config_set_stereo(GtkWidget *but, gpointer data) { - gint i=(gint)data; + gint i=(gint)((long)data); /* NPM: "(gint)((long)data)" suppress "config.c:49: warning: cast from pointer to integer of different size" */ config_stereo[i]=GTK_TOGGLE_BUTTON(but)->active; } diff --git a/envy24control/envy24control.1 b/envy24control/envy24control.1 index bfa2ad0..0dc2cc5 100644 --- a/envy24control/envy24control.1 +++ b/envy24control/envy24control.1 @@ -74,7 +74,7 @@ Values >20 are interpreted as screen pixels. \fI\-t\fP tall\-equal mixer heights Using \fI\-t\fP 1 enlarges the channel mixers to the same height as the digital mixer. With values >1 the height of all mixers is increased in stages. -\fI\-t\fP 0 is the default, set for minimum window height. +\fI\-t\fP 1 is the default in version 1.X; for old behavior use "\fI\-t\fP 0" .SH "SEE ALSO" \fB alsamixer(1), @@ -83,7 +83,23 @@ gamix(1) \fP .SH "AUTHOR" -\fBenvy24control\fP is by Jaroslav Kysela -This document is by James Tappin . -Updated by Dirk Kalis . -Options\fI\-w\ and \fI\-t\fP added Alan Horstmann + +\fBenvy24control\fP is originally by Jaroslav Kysela . +with layout changes by Søren Wedel Nielsen, and volume control code by +Takashi Iwai. + +Options \fI\-w\fP and \fI\-t\fP added Alan Horstmann +. + +Niels Mayer ( http://nielsmayer.com ) (1) Implemented "Peak Hold" +functionality in meters and reimplemented meters for increased efficiency +and lower X resource usage. (2) All volumes are represented as decibels, +including the 0 to -48dB range of the hardware peak-meters, the 0 -to- +-144dB attenuation for all inputs to the digital mixer, the 0 -to- -63dB +attenuation of the analog DAC, and the +18 -to- -63dB +attenuation/amplification of the analog ADC. (3) All gtk "scale" widgets +have dB legends; the "PageUp" "PageDown" keys allow rapid movement between +the marked levels. + +This document is by James Tappin . Updated by Dirk +Kalis . diff --git a/envy24control/envy24control.c b/envy24control/envy24control.c index 0b2749e..028959d 100644 --- a/envy24control/envy24control.c +++ b/envy24control/envy24control.c @@ -1,7 +1,9 @@ /***************************************************************************** - envy24control.c - Env24 chipset (ICE1712) control utility + envy24control.c - Envy24 chipset (ICE1712) control utility Copyright (C) 2000 by Jaroslav Kysela + volume.c: added by Takashi Iwai + (2003/03/22) Changed to hbox/vbox layout. Copyright (C) 2003 by Søren Wedel Nielsen @@ -12,6 +14,18 @@ New options added: 'w' to set initial window pixel width and 't' for tall equal mixer height style. Copyright (C) 2005 by Alan Horstmann + (July 2010) (0) After a decade, incremented version to 1.0. (1) + Implemented "Peak Hold" functionality in meters and reimplemented meters + for increased efficiency and lower X resource usage. (2) All volumes are + represented as decibels, including the 0 to -48dB range of the hardware + peak-meters, the 0 -to- -144dB attenuation for all inputs to the digital + mixer, the 0 -to- -63dB attenuation of the analog DAC, and the +18 -to- + -63dB attenuation/amplification of the analog ADC. (3) All gtk "scale" + widgets have dB legends; the "PageUp" "PageDown" keys allow rapid + movement between the marked levels. (4) Got rid of myriad compiler + warnings and other minor fixes across codebase. + Copyright (C) 2010 Niels Mayer ( http://nielsmayer.com ) + 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 @@ -34,7 +48,7 @@ #include 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; +int card_is_dmx6fire = FALSE, tall_equal_mixer_ht = 1; char *profiles_file_name, *default_profile; ice1712_eeprom_t card_eeprom; @@ -47,6 +61,10 @@ GtkWidget *mixer_clear_peaks_button; GtkWidget *mixer_drawing[20]; GtkObject *mixer_adj[20][2]; GtkWidget *mixer_vscale[20][2]; +GtkWidget *mixer_label[20][2]; /* NPM: labels for L/R dB attenuation into digital mixer: see "Monitor Inputs" and "Monitor PCMs" panels */ +GtkWidget *peak_label[MULTI_TRACK_PEAK_CHANNELS]; /* NPM: labels for dBFS peaks for all inputs/outputs of digital mixer: see "Monitor Inputs" and "Monitor PCMs" panels */ +GtkWidget *adc_peak_label[MAX_INPUT_CHANNELS] = {NULL, }; /* NPM: labels for dBFS peak levels of ADC inputs: see "Analog Volume" panel */ +GtkWidget *dac_peak_label[MAX_OUTPUT_CHANNELS] = {NULL, }; /* NPM: labels for dBFS peak levels of DAC outputs: see "Analog Volume" panel */ GtkWidget *mixer_mute_toggle[20][2]; GtkWidget *mixer_stereo_toggle[20]; @@ -118,9 +136,9 @@ GtkWidget *hw_phono_input_off_radio; GtkObject *av_dac_volume_adj[10]; GtkObject *av_adc_volume_adj[10]; GtkObject *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_volume_label[10]; +GtkWidget *av_adc_volume_label[10]; +GtkWidget *av_ipga_volume_label[10]; GtkWidget *av_dac_sense_radio[10][4]; GtkWidget *av_adc_sense_radio[10][4]; @@ -132,7 +150,6 @@ struct profile_button { GtkWidget *active_button = NULL; GtkObject *card_number_adj; - static void create_mixer_frame(GtkWidget *box, int stream) { GtkWidget *vbox; @@ -142,7 +159,6 @@ static void create_mixer_frame(GtkWidget *box, int stream) GtkObject *adj; GtkWidget *vscale; GtkWidget *drawing; - GtkWidget *label; GtkWidget *toggle; char str[64], drawname[32]; @@ -180,17 +196,23 @@ static void create_mixer_frame(GtkWidget *box, int stream) gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); - hbox = gtk_hbox_new(FALSE, 2); + hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); - adj = gtk_adjustment_new(96, 0, 96, 1, 16, 0); + /* + * NPM: Left-channel scale widgets to adjust 24 bit attenuation of + * each input into ice1712's on-chip digital mixer. + */ + adj = gtk_adjustment_new(MAX_MIXER_ATTENUATION_VALUE, 0, MAX_MIXER_ATTENUATION_VALUE, 1, ANALOG_GAIN_STEP_SIZE, 0); /* NPM: using step size of 12 gives -18dB step-size */ mixer_adj[stream-1][0] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); + gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); /* NPM: don't draw value since printing dB values via mixer_adjust() */ mixer_vscale[stream-1][0] = vscale; + /* NPM: above, set step size of 12 ==> -18dB step-size. Place dB-labelled markers at those locations */ + draw_24bit_attenuator_scale_markings(GTK_SCALE(vscale), GTK_POS_LEFT, (stream%2)); gtk_widget_show(vscale); gtk_box_pack_start(GTK_BOX(hbox), vscale, TRUE, FALSE, 0); - gtk_scale_set_value_pos(GTK_SCALE(vscale), GTK_POS_BOTTOM); gtk_scale_set_digits(GTK_SCALE(vscale), 0); gtk_signal_connect(GTK_OBJECT(adj), "value_changed", GTK_SIGNAL_FUNC(mixer_adjust), @@ -210,38 +232,49 @@ static void create_mixer_frame(GtkWidget *box, int stream) gtk_signal_connect(GTK_OBJECT(drawing), "configure_event", GTK_SIGNAL_FUNC(level_meters_configure_event), NULL); gtk_widget_set_events(drawing, GDK_EXPOSURE_MASK); - gtk_widget_set_usize(drawing, 36, (60 * tall_equal_mixer_ht + 204)); - gtk_box_pack_start(GTK_BOX(vbox1), drawing, FALSE, FALSE, 1); - - label = gtk_label_new(""); - gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(vbox1), label, TRUE, TRUE, 2); - - adj = gtk_adjustment_new(96, 0, 96, 1, 16, 0); + gtk_widget_set_usize(drawing, 24, (60 * tall_equal_mixer_ht + 204)); + gtk_box_pack_end(GTK_BOX(vbox1), drawing, FALSE, FALSE, 1); + + /* + * NPM: Right-channel scale widgets to adjust 24bit attenuation of + * each input into ice1712's on-chip digital mixer. + */ + adj = gtk_adjustment_new(MAX_MIXER_ATTENUATION_VALUE, 0, MAX_MIXER_ATTENUATION_VALUE, 1, ANALOG_GAIN_STEP_SIZE, 0); mixer_adj[stream-1][1] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); + gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); /* NPM: don't draw value since printing dB values via mixer_adjust() */ mixer_vscale[stream-1][1] = vscale; - gtk_widget_show(vscale); + draw_24bit_attenuator_scale_markings(GTK_SCALE(vscale), GTK_POS_RIGHT, ((stream-1)%2)); + gtk_widget_show(vscale); gtk_box_pack_start(GTK_BOX(hbox), vscale, TRUE, FALSE, 0); - gtk_scale_set_value_pos(GTK_SCALE(vscale), GTK_POS_BOTTOM); gtk_scale_set_digits(GTK_SCALE(vscale), 0); gtk_signal_connect(GTK_OBJECT(adj), "value_changed", GTK_SIGNAL_FUNC(mixer_adjust), (gpointer)(long)((stream << 16) + 1)); + + /* NPM: Labels to display the retained peak levels gathered from ice1712's hardware metering */ + peak_label[stream-1] = gtk_label_new("(Off)"); + gtk_widget_modify_font(peak_label[stream-1], pango_font_description_from_string ("Monospace")); + gtk_widget_show(peak_label[stream-1]); + gtk_box_pack_start(GTK_BOX(vbox), peak_label[stream-1], TRUE, FALSE, 0); hbox = gtk_hbox_new(TRUE, 0); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 0); - label = gtk_label_new("Left"); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); - - label = gtk_label_new("Right"); - gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); - gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); + /* NPM: Labels to display attenuation of Left input into digital mixer: see mixer.c:mixer_adjust() */ + mixer_label[stream-1][0] = gtk_label_new("(Off)"); /* NPM: note that all but the "(Off)" values get refreshed at startup */ + gtk_misc_set_alignment(GTK_MISC(mixer_label[stream-1][0]), 0, 0.5); + gtk_widget_modify_font(mixer_label[stream-1][0], pango_font_description_from_string ("Monospace")); + gtk_widget_show(mixer_label[stream-1][0]); + gtk_box_pack_start(GTK_BOX(hbox), mixer_label[stream-1][0], FALSE, TRUE, 0); + + /* NPM: Labels to display attenuation of Right input into digital mixer: see mixer.c:mixer_adjust() */ + mixer_label[stream-1][1] = gtk_label_new("(Off)"); /* NPM: note that all but the "(Off)" values get refreshed at startup */ + gtk_misc_set_alignment(GTK_MISC(mixer_label[stream-1][1]), 1, 0.5); + gtk_widget_modify_font(mixer_label[stream-1][1], pango_font_description_from_string ("Monospace")); + gtk_widget_show(mixer_label[stream-1][1]); + gtk_box_pack_start(GTK_BOX(hbox), mixer_label[stream-1][1], FALSE, TRUE, 0); toggle = gtk_toggle_button_new_with_label("L/R Gang"); mixer_stereo_toggle[stream-1] = toggle; @@ -249,9 +282,9 @@ static void create_mixer_frame(GtkWidget *box, int stream) gtk_box_pack_end(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); /* gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), TRUE); */ gtk_signal_connect(GTK_OBJECT(toggle), "toggled", - GTK_SIGNAL_FUNC(config_set_stereo), (gpointer)stream-1); + GTK_SIGNAL_FUNC(config_set_stereo), (gpointer)(long)(stream - 1)); /* NPM: use (long) to fix "envy24control.c:251: warning: cast to pointer from integer of different size" */ - hbox = gtk_hbox_new(TRUE, 6); + hbox = gtk_hbox_new(TRUE, 3); gtk_widget_show(hbox); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); @@ -317,7 +350,7 @@ static void create_inputs_mixer(GtkWidget *main, GtkWidget *notebook, int page) 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)) + if (mixer_stream_is_active(stream)) create_mixer_frame(hbox, stream); } for(stream = (MAX_PCM_OUTPUT_CHANNELS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS + 1); \ @@ -667,7 +700,7 @@ static void create_rate_state(GtkWidget *box) gtk_container_add(GTK_CONTAINER(frame), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), 6); - check = gtk_check_button_new_with_label("Locked"); + check = gtk_check_button_new_with_label("Multi Track\nRate Locking"); hw_rate_locking_check = check; gtk_widget_show(check); gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0); @@ -676,7 +709,7 @@ static void create_rate_state(GtkWidget *box) (gpointer)"locked"); - check = gtk_check_button_new_with_label("Reset"); + check = gtk_check_button_new_with_label("Multi Track\nRate Reset"); hw_rate_reset_check = check; gtk_widget_show(check); gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0); @@ -1495,40 +1528,50 @@ static void create_analog_volume(GtkWidget *main, GtkWidget *notebook, int page) /* create DAC */ for(i = 0; i < envy_dac_volumes(); i++) { char name[32]; - sprintf(name, "DAC %d", i); + sprintf(name, "DAC %d", i+1); /* NPM: for consistency w/ other panels, start w/ "DAC 1" */ frame = gtk_frame_new(name); gtk_widget_show(frame); //gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); - gtk_container_set_border_width(GTK_CONTAINER(frame), 6); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); /* Add friendly labels for DMX 6Fires */ if(card_is_dmx6fire && (i < 6)){ label = gtk_label_new(dmx6fire_outputs[i]); gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 6); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); } - adj = gtk_adjustment_new(0, -(envy_dac_max()), 0, 1, 16, 0); + /* NPM: display peak levels on DAC's in "Analog Volume" panel */ + if (i < MAX_OUTPUT_CHANNELS) { /* make sure within bounds of dac_peak_label[] */ + dac_peak_label[i] = label = gtk_label_new("(Off)"); + gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); + } + + adj = gtk_adjustment_new(0, -(envy_dac_max()), 0, 1, ANALOG_GAIN_STEP_SIZE, 0); /* NPM: using step size of 12 gives -6dB step-size */ av_dac_volume_adj[i] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); - gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); + /* NPM: above, set step size of 12 ==> -6dB step-size. Place dB-labelled markers at those locations */ + draw_dac_scale_markings(GTK_SCALE(vscale), (i%2) ? GTK_POS_RIGHT : GTK_POS_LEFT); + gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); /* NPM: don't draw scale value since we're displaying in dB's */ gtk_widget_show(vscale); - gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 6); + gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 0); gtk_scale_set_digits(GTK_SCALE(vscale), 0); gtk_signal_connect(GTK_OBJECT(adj), "value_changed", GTK_SIGNAL_FUNC(dac_volume_adjust), (gpointer)(long)(i)); - label = gtk_label_new("000"); - av_dac_volume_label[i] =(GtkLabel *)label; + av_dac_volume_label[i] = label = gtk_label_new("-63.5"); + gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 6); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); if (i >= envy_dac_senses()) @@ -1551,41 +1594,51 @@ static void create_analog_volume(GtkWidget *main, GtkWidget *notebook, int page) /* create ADC */ for (i = 0; i < envy_adc_volumes(); i++) { char name[32]; - sprintf(name, "ADC %d", i); + sprintf(name, "ADC %d", i+1); /* NPM: for consistency w/ other panels, start w/ "ADC 1" */ frame = gtk_frame_new(name); gtk_widget_show(frame); //gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); - gtk_container_set_border_width(GTK_CONTAINER(frame), 6); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); /* Add friendly labels for DMX 6Fires */ if(card_is_dmx6fire && (i < 6)){ label = gtk_label_new(dmx6fire_inputs[i]); gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 6); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); + } + + /* NPM: display peak levels on ADC's in "Analog Volume" panel */ + if (i < MAX_INPUT_CHANNELS) { /* make sure within bounds of adc_peak_label[] */ + adc_peak_label[i] = label = gtk_label_new("(Off)"); + gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); } - adj = gtk_adjustment_new(0, -(envy_adc_max()), 0, 1, 16, 0); + adj = gtk_adjustment_new(0, -(envy_adc_max()), 0, 1, ANALOG_GAIN_STEP_SIZE, 0); /* using step size of 12 gives -6dB step-size */ av_adc_volume_adj[i] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); - gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); + /* NPM: above, set step size of 12 ==> -6dB step-size. Place dB-labelled markers at those locations */ + draw_adc_scale_markings(GTK_SCALE(vscale), (i%2) ? GTK_POS_RIGHT : GTK_POS_LEFT); + gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); /* NPM: don't draw scale value since we're displaying in dB's */ gtk_widget_show(vscale); - gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 6); - gtk_scale_set_value_pos(GTK_SCALE(vscale), GTK_POS_BOTTOM); + gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 0); gtk_scale_set_digits(GTK_SCALE(vscale), 0); gtk_signal_connect(GTK_OBJECT(adj), "value_changed", GTK_SIGNAL_FUNC(adc_volume_adjust), (gpointer)(long)(i)); - label = gtk_label_new("000"); - av_adc_volume_label[i] =(GtkLabel *)label; - gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 6); + /* NPM */ + av_adc_volume_label[i] = label = gtk_label_new("-63.5"); + gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); if (i >= envy_adc_senses()) continue; @@ -1612,36 +1665,35 @@ static void create_analog_volume(GtkWidget *main, GtkWidget *notebook, int page) gtk_widget_show(frame); //gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); - gtk_container_set_border_width(GTK_CONTAINER(frame), 6); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(frame), vbox); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); /* Add friendly labels for DMX 6Fires */ if(card_is_dmx6fire && (i < 6)){ label = gtk_label_new(dmx6fire_inputs[i]); gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 6); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3); } - adj = gtk_adjustment_new(0, -36, 0, 1, 16, 0); + adj = gtk_adjustment_new(0, -36, 0, 1, 6, 0); av_ipga_volume_adj[i] = adj; vscale = gtk_vscale_new(GTK_ADJUSTMENT(adj)); gtk_scale_set_draw_value(GTK_SCALE(vscale), FALSE); gtk_widget_show(vscale); - gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 6); - gtk_scale_set_value_pos(GTK_SCALE(vscale), GTK_POS_BOTTOM); + gtk_box_pack_start(GTK_BOX(vbox), vscale, TRUE, TRUE, 3); gtk_scale_set_digits(GTK_SCALE(vscale), 0); gtk_signal_connect(GTK_OBJECT(adj), "value_changed", GTK_SIGNAL_FUNC(ipga_volume_adjust), (gpointer)(long)(i)); - label = gtk_label_new("000"); - av_ipga_volume_label[i] = (GtkLabel *)label; + av_ipga_volume_label[i] = label = gtk_label_new("-63.5"); + gtk_widget_modify_font(label, pango_font_description_from_string ("Monospace")); gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 6); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3); } } @@ -1976,17 +2028,16 @@ static void create_outer(GtkWidget *main) gtk_box_pack_start(GTK_BOX(vbox), hbox1, TRUE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox1), 6); - label = gtk_label_new("Left"); + peak_label[IDX_LMIX] = label = gtk_label_new("(Off)"); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, TRUE, 0); - label = gtk_label_new("Right"); + peak_label[IDX_RMIX] = label = gtk_label_new("(Off)"); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, TRUE, 0); - mixer_clear_peaks_button = gtk_button_new_with_label("Reset Peaks"); gtk_widget_show(mixer_clear_peaks_button); gtk_box_pack_start(GTK_BOX(vbox), mixer_clear_peaks_button, TRUE, FALSE, 0); @@ -2031,6 +2082,17 @@ static void usage(void) fprintf(stderr, "\t-t, --tall_eq_mixer_heights\tSet taller height mixer displays (1-9)\n"); } +/* NPM for efficiency&power-savings, replaced multiple 40ms&100ms timeouts + for each of the callbacks contained here, with a single 100ms one which + calls gtk_timeout_add(100, (GtkFunction)envy24control_poll, ...) */ +void envy24control_poll() { + level_meters_timeout_callback(NULL); + master_clock_status_timeout_callback(NULL); + internal_clock_status_timeout_callback(NULL); + rate_locking_status_timeout_callback(NULL); + rate_reset_status_timeout_callback(NULL); +} + int main(int argc, char **argv) { GtkWidget *notebook; @@ -2249,6 +2311,8 @@ int main(int argc, char **argv) if (midi_channel >= 0) midi_fd = midi_init(argv[0], midi_channel, midi_enhanced); + gtk_timeout_add(100, (GtkFunction)envy24control_poll, (gpointer) NULL); /* NPM for efficiency&power-savings, replaced multiple 40ms&100ms timeouts with this single one */ + 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); @@ -2303,11 +2367,6 @@ int main(int argc, char **argv) if (midi_fd >= 0) { gdk_input_add(midi_fd, GDK_INPUT_READ, midi_process, NULL); } - gtk_timeout_add(40, level_meters_timeout_callback, NULL); - gtk_timeout_add(100, master_clock_status_timeout_callback, NULL); - gtk_timeout_add(100, internal_clock_status_timeout_callback, NULL); - gtk_timeout_add(100, rate_locking_status_timeout_callback, NULL); - gtk_timeout_add(100, rate_reset_status_timeout_callback, NULL); gtk_widget_show(window); diff --git a/envy24control/envy24control.h b/envy24control/envy24control.h index f5c1e39..5e34d19 100644 --- a/envy24control/envy24control.h +++ b/envy24control/envy24control.h @@ -58,6 +58,52 @@ #define MAX_SPDIF_CHANNELS 2 /* max number of PCM output channels */ #define MAX_PCM_OUTPUT_CHANNELS 8 +/* NPM: the digital mixer max-attenuation in dB, per snd ice1712 architecture diagram + http://nielsmayer.com/npm/envy24mixer-architecture.png + taken from http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf */ +#define MAX_MIXER_ATTENUATION_DB 144.0 /* for -144dB */ +#define MIN_MIXER_ATTENUATION_DB 0.0 /* for 0dB */ +/* NPM: the control values for the digital mixer are 0-96 and not the + dB attenuation values of MAX_MIXER_ATTENUATION_DB/MIN_MIXER_ATTENUATION_DB + The following values comprise all the signals mixed in the ice1712's digital + mixer: MULTI_PLAYBACK_VOLUME, HW_MULTI_CAPTURE_VOLUME, IEC958_MULTI_CAPTURE_VOLUME: + > amixer -c M66 cget iface=MIXER,name='Multi Playback Volume' + numid=11,iface=MIXER,name='Multi Playback Volume' + ; type=INTEGER,access=rw---R--,values=2,min=0,max=96,step=0 + : values=78,67 + | dBscale-min=-144.00dB,step=1.50dB,mute=0 + > amixer -c M66 cget iface=MIXER,name="H/W Multi Capture Volume" + numid=27,iface=MIXER,name='H/W Multi Capture Volume' + ; type=INTEGER,access=rw---R--,values=2,min=0,max=96,step=0 + : values=0,0 + | dBscale-min=-144.00dB,step=1.50dB,mute=0 + > amixer -c M66 cget iface=MIXER,name="IEC958 Multi Capture Volume" + numid=31,iface=MIXER,name='IEC958 Multi Capture Volume' + ; type=INTEGER,access=rw------,values=2,min=0,max=96,step=0 + : values=16,0 +*/ +#define MAX_MIXER_ATTENUATION_VALUE 96 /* for -144dB */ +#define MIN_MIXER_ATTENUATION_VALUE 0 /* for 0dB */ + +/* + * NPM: DAC and ADC constants + */ +#define MIN_ADC_GAIN -63 +#define MAX_ADC_GAIN 18 +#define MIN_DAC_GAIN -63 +#define MAX_DAC_GAIN 0 +#define ANALOG_GAIN_STEP_SIZE 12 /* this vale gives a known -18dB step size for 24 bit attenuators, -6dB step for 16 bit attenuators */ + +/* + * NPM: For peak meters + */ +#define MULTI_TRACK_PEAK_CHANNELS 22 +#define IDX_LMIX 20 +#define IDX_RMIX 21 + +/* + * NPM: + */ typedef struct { unsigned int subvendor; /* PCI[2c-2f] */ @@ -87,6 +133,11 @@ extern GtkWidget *mixer_clear_peaks_button; extern GtkWidget *mixer_drawing[20]; extern GtkObject *mixer_adj[20][2]; extern GtkWidget *mixer_vscale[20][2]; +extern GtkWidget *mixer_label[20][2]; /* NPM */ +extern GtkWidget *peak_label[MULTI_TRACK_PEAK_CHANNELS]; /* NPM */ +extern GtkWidget *adc_peak_label[MAX_INPUT_CHANNELS]; /* NPM */ +extern GtkWidget *dac_peak_label[MAX_OUTPUT_CHANNELS]; /* NPM */ + extern GtkWidget *mixer_solo_toggle[20][2]; extern GtkWidget *mixer_mute_toggle[20][2]; extern GtkWidget *mixer_stereo_toggle[20]; @@ -154,9 +205,9 @@ extern GtkWidget *input_interface_wavetable; extern GtkObject *av_dac_volume_adj[]; extern GtkObject *av_adc_volume_adj[]; extern GtkObject *av_ipga_volume_adj[]; -extern GtkLabel *av_dac_volume_label[]; -extern GtkLabel *av_adc_volume_label[]; -extern GtkLabel *av_ipga_volume_label[]; +extern GtkWidget *av_dac_volume_label[]; +extern GtkWidget *av_adc_volume_label[]; +extern GtkWidget *av_ipga_volume_label[]; extern GtkWidget *av_dac_sense_radio[][4]; extern GtkWidget *av_adc_sense_radio[][4]; @@ -241,3 +292,9 @@ void adc_sense_toggled(GtkWidget *togglebutton, gpointer data); void control_input_callback(gpointer data, gint source, GdkInputCondition condition); void mixer_input_callback(gpointer data, gint source, GdkInputCondition condition); +/* NPM: volume/db-related stuff added to volume.c */ +char* mixer_volume_to_db(int ival); +char* peak_level_to_db(int ival); +void draw_24bit_attenuator_scale_markings(GtkScale *scale, GtkPositionType position, int draw_legend_p); +void draw_dac_scale_markings(GtkScale *scale, GtkPositionType position); +void draw_adc_scale_markings(GtkScale *scale, GtkPositionType position); diff --git a/envy24control/levelmeters.c b/envy24control/levelmeters.c index fa44979..fc20fe8 100644 --- a/envy24control/levelmeters.c +++ b/envy24control/levelmeters.c @@ -1,6 +1,8 @@ /***************************************************************************** levelmeters.c - Stereo level meters Copyright (C) 2000 by Jaroslav Kysela + Missing peak-level meter feature implented by NPM: + Copyright (C) 2010 by Niels Mayer ( http://nielsmayer.com ). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -19,39 +21,65 @@ #include "envy24control.h" -static GdkGC *penGreenShadow[21] = { NULL, }; +static GdkGC *penWhiteLight[21] = { NULL, }; static GdkGC *penGreenLight[21] = { NULL, }; -static GdkGC *penOrangeShadow[21] = { NULL, }; +static GdkGC *penBackground[21] = { NULL, }; static GdkGC *penOrangeLight[21] = { NULL, }; -static GdkGC *penRedShadow[21] = { NULL, }; static GdkGC *penRedLight[21] = { NULL, }; static GdkPixmap *pixmap[21] = { NULL, }; static snd_ctl_elem_value_t *peaks; extern int input_channels, output_channels, pcm_output_channels, spdif_channels, view_spdif_playback; -static void update_peak_switch(void) -{ +static void update_peak_switch(void) { int err; if ((err = snd_ctl_elem_read(ctl, peaks)) < 0) g_print("Unable to read peaks: %s\n", snd_strerror(err)); } -static void get_levels(int idx, int *l1, int *l2) -{ - *l1 = *l2 = 0; - - if (idx == 0) { - *l1 = snd_ctl_elem_value_get_integer(peaks, 20); - *l2 = snd_ctl_elem_value_get_integer(peaks, 21); - } else { - *l1 = *l2 = snd_ctl_elem_value_get_integer(peaks, idx - 1); - } +/* + * Niels Mayer (NPM) Jul-11-10: Fixing https://bugzilla.redhat.com/show_bug.cgi?id=602903 + * by implementing peak-level meters. The http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf + * soundchip provides hardware level meters and these are returned via ALSA as such: + * >> amixer -c M66 cget iface=PCM,name='Multi Track Peak',numid=45 + * ; type=INTEGER,access=r-------,values=22,min=0,max=255,step=0 + * : values=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,198,255,198 + * These globals store the peak levels of the meters across callbacks. + */ +#define RESET -1 +// NPM: Note special case in original envy24control code, which puts stereo mix track at +// "index 0" which is actually "(peaks, 20)" and "(peaks, 21)" cases below +// thus peak_lmix <--> peak_levels[IDX_LMIX] ; peak_rmix <--> peak_levels[IDX_RMIX] +static int peak_levels[MULTI_TRACK_PEAK_CHANNELS] = {0,}; +static int peak_changed[MULTI_TRACK_PEAK_CHANNELS] = {0,}; +static int previous_levels[MULTI_TRACK_PEAK_CHANNELS] = {0,}; +/* NPM: changed for https://bugzilla.redhat.com/show_bug.cgi?id=602903 */ +static void get_levels(int idx, int *l1, int *l2) { + *l1 = *l2 = 0; + if (idx == 0) { /* "if (stereo)" -- special case idx=0 as digital mix pair */ + if ((peak_changed[IDX_LMIX] != RESET) && (peak_changed[IDX_RMIX] != RESET)) { /* don't change values if doing "Reset Peaks" */ + if ((*l1 = snd_ctl_elem_value_get_integer(peaks, IDX_LMIX)) > peak_levels[IDX_LMIX]) { + peak_levels[IDX_LMIX] = (*l1); + peak_changed[IDX_LMIX] = TRUE; + } + if ((*l2 = snd_ctl_elem_value_get_integer(peaks, IDX_RMIX)) > peak_levels[IDX_RMIX]) { + peak_levels[IDX_RMIX] = (*l2); + peak_changed[IDX_RMIX] = TRUE; + } + } + } + else { + if (peak_changed[idx-1] != RESET) { + if ((*l1 = snd_ctl_elem_value_get_integer(peaks, idx - 1)) > peak_levels[idx - 1]) { + peak_levels[idx - 1] = (*l1); + peak_changed[idx - 1] = TRUE; + } + } + } } -static GdkGC *get_pen(int idx, int nRed, int nGreen, int nBlue) -{ +static GdkGC *get_pen(int idx, int nRed, int nGreen, int nBlue) { GdkColor *c; GdkGC *gc; @@ -65,8 +93,7 @@ static GdkGC *get_pen(int idx, int nRed, int nGreen, int nBlue) return gc; } -static int get_index(gchar *name) -{ +static int get_index(const gchar *name) { int result; if (!strcmp(name, "DigitalMixer")) @@ -79,77 +106,309 @@ static int get_index(gchar *name) return result; } -static void redraw_meters(int idx, int width, int height, int level1, int level2) -{ - int stereo = idx == 0; - int segment_width = stereo ? (width / 2) - 8 : width - 12; - int segments = (height - 6) / 4; - int green_segments = (segments / 4) * 3; - int red_segments = 2; - int orange_segments = segments - green_segments - red_segments; - int seg; - int segs_on1 = ((segments * level1) + 128) / 255; - int segs_on2 = ((segments * level2) + 128) / 255; - - // g_print("segs_on1 = %i (%i), segs_on2 = %i (%i)\n", segs_on1, level1, segs_on2, level2); - for (seg = 0; seg < green_segments; seg++) { - gdk_draw_rectangle(pixmap[idx], - segs_on1 > 0 ? penGreenLight[idx] : penGreenShadow[idx], - TRUE, - 6, 3 + ((segments - seg - 1) * 4), - segment_width, - 3); - if (stereo) - gdk_draw_rectangle(pixmap[idx], - segs_on2 > 0 ? penGreenLight[idx] : penGreenShadow[idx], - TRUE, - 2 + (width / 2), - 3 + ((segments - seg - 1) * 4), - segment_width, - 3); - segs_on1--; - segs_on2--; - } - for (seg = green_segments; seg < green_segments + orange_segments; seg++) { - gdk_draw_rectangle(pixmap[idx], - segs_on1 > 0 ? penOrangeLight[idx] : penOrangeShadow[idx], - TRUE, - 6, 3 + ((segments - seg - 1) * 4), - segment_width, - 3); - if (stereo) - gdk_draw_rectangle(pixmap[idx], - segs_on2 > 0 ? penOrangeLight[idx] : penOrangeShadow[idx], - TRUE, - 2 + (width / 2), - 3 + ((segments - seg - 1) * 4), - segment_width, - 3); - segs_on1--; - segs_on2--; - } - for (seg = green_segments + orange_segments; seg < segments; seg++) { - gdk_draw_rectangle(pixmap[idx], - segs_on1 > 0 ? penRedLight[idx] : penRedShadow[idx], - TRUE, - 6, 3 + ((segments - seg - 1) * 4), - segment_width, - 3); - if (stereo) - gdk_draw_rectangle(pixmap[idx], - segs_on2 > 0 ? penRedLight[idx] : penRedShadow[idx], - TRUE, - 2 + (width / 2), - 3 + ((segments - seg - 1) * 4), - segment_width, - 3); - segs_on1--; - segs_on2--; - } +/* + * NPM: per http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf the + * the values in each entry of iface=PCM,name='Multi Track Peak',numid=45 + * represent: "Peak data derived from the absolute value of 9 msb. 00h + * min - FFh max volume. Reading the register resets the meter to 00h." + */ +#define NUM_METERING_LEVELS 256 +#define GET_COLOR_FOR_PEAKLEVEL(level) \ + (level > 250) \ + ? (penRedLight[idx]) \ + : ((level > 200) \ + ? (penOrangeLight[idx]) \ + : ((level > 150) \ + ? (penWhiteLight[idx]) \ + : (penGreenLight[idx]))) + +/* + * NPM: Called by redraw_meters(), this is a special case for when "Reset + * Peaks" is clicked: then just refresh meters, ignore value, clear RESET + * and let the next pass-through draw for the first time in the cleared + * meter... + */ +static void draw_meters_reset(int idx, int width, int height, int level1, int level2, + int stereo, int segment_width) { + GdkColor color; + GtkWidget* lbl; + gdk_color_parse("black", &color); + + gdk_draw_rectangle(pixmap[idx], + penBackground[idx], + TRUE, + //X + 6, + //Y + 0, + //WIDTH + segment_width, + //HEIGHT + height + ); + /* NPM: Reset peak labels for "Monitor Inputs" and "Monitor PCMs" panels */ + gtk_label_set_text(GTK_LABEL((stereo) ? peak_label[IDX_LMIX] : peak_label[idx-1]), + peak_level_to_db((stereo) ? peak_levels[IDX_LMIX] : peak_levels[idx-1])); /* put new value in label */ + gtk_widget_modify_fg((stereo) ? peak_label[IDX_LMIX] : peak_label[idx-1], + GTK_STATE_NORMAL, &color); + + /* NPM: Reset "Analog Volume" panel's peak levels -- never happens for "stereo" case */ + if (!stereo) { + /* NPM: Reset peak labels for DACs in "Analog Volume" panel */ + if (((idx-1) >= 0) && ((idx-1) < envy_dac_volumes())) { /* index 0-8 corresponds to one of the eight DAC's */ + if (((idx-1) < MAX_OUTPUT_CHANNELS) /* make sure within bounds of dac_peak_label[] */ + && (lbl = dac_peak_label[idx-1]) != NULL) { + gtk_label_set_text(GTK_LABEL(lbl), peak_level_to_db(peak_levels[idx-1])); /* put new value in label */ + gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color); /* reset color */ + } + } + /* NPM: Reset peak labels for ADCs in "Analog Volume" panel */ + else if (((idx-1) >= (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) /* ADC channels begin at 11 = MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS */ + && ((idx-1) < (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS + envy_adc_volumes()))) { /* ADC channels end at 19 */ + if ((((idx-1) - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) < MAX_INPUT_CHANNELS) /* make sure within bounds of adc_peak_label[] */ + && (lbl = adc_peak_label[(idx-1) - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)]) != NULL) { + gtk_label_set_text(GTK_LABEL(lbl), peak_level_to_db(peak_levels[idx-1])); /* put new value in label */ + gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color); /* reset color */ + } + } + } + if (stereo) { + gdk_draw_rectangle(pixmap[idx], + penBackground[idx], + TRUE, + //X + 2 + (width / 2), + //Y + 0, + //WIDTH + segment_width, + //HEIGHT + height + ); + gtk_label_set_text(GTK_LABEL(peak_label[IDX_RMIX]), + peak_level_to_db(peak_levels[IDX_RMIX])); /* put new value in label */ + gtk_widget_modify_fg(peak_label[IDX_RMIX], GTK_STATE_NORMAL, &color); + peak_changed[IDX_LMIX] = FALSE; + peak_changed[IDX_RMIX] = FALSE; + } + else { + peak_changed[idx-1] = FALSE; + } } -gint level_meters_configure_event(GtkWidget *widget, GdkEventConfigure *event) -{ +/* + * NPM: Called through redraw_meters(), via draw_meters_and_peaks(), + * this handles updating gtk labels/colors related to updated peak values. + * Note 'index' [0 to MULTI_TRACK_PEAK_CHANNELS-1] into global peak_label[] + * which is initialized by envy24control.c:create_mixer_frame(). + */ +static void draw_peak_labels(int index, int stereo, int peak1_level, int peak2_level) { + GdkColor color; + GtkWidget* lbl; + + gtk_label_set_text(GTK_LABEL((stereo) ? peak_label[IDX_LMIX] : peak_label[index]), + peak_level_to_db(peak1_level)); /* put new value in label */ + if (peak1_level >= 255) { /* if at 0dB, make label red; RESET reverts to normal color */ + gdk_color_parse("red", &color); + gtk_widget_modify_fg((stereo) ? peak_label[IDX_LMIX] : peak_label[index], + GTK_STATE_NORMAL, &color); + } + + /* NPM: Update "Analog Volume" panel's peak levels: the normal non-"stereo" + case of single channel peak monitors of input or PCMs */ + if (!stereo) { + /* NPM: Update peak labels for DACs in "Analog Volume" panel */ + if ((index >= 0) && (index < envy_dac_volumes())) { /* index 0-8 corresponds to one of the eight DAC's */ + if ((index < MAX_OUTPUT_CHANNELS) /* make sure within bounds of dac_peak_label[] */ + && (lbl = dac_peak_label[index]) != NULL) { + gtk_label_set_text(GTK_LABEL(lbl), peak_level_to_db(peak1_level)); /* put new value in label */ + if (peak1_level >= 255) + gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color); /* color set above in "if peak1_level >= 255" */ + } + } + /* NPM: Update peak labels for ADCs in "Analog Volume" panel */ + else if ((index >= (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) /* ADC channels begin at 11 = MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS */ + && (index <= (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS + envy_adc_volumes()))) { /* ADC channels end at 19 */ + if (((index - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) < MAX_INPUT_CHANNELS) /* make sure within bounds of adc_peak_label[] */ + && (lbl = adc_peak_label[index - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)]) != NULL) { + gtk_label_set_text(GTK_LABEL(lbl), peak_level_to_db(peak1_level)); /* put new value in label */ + if (peak1_level >= 255) + gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color); /* color set above in "if peak1_level >= 255" */ + } + } + } + /* Handle special "stereo" case for right-channel of digital mixer */ + else if (peak_changed[IDX_RMIX]) { //for stereo, draw RMIX, but skip redraw if same + gtk_label_set_text(GTK_LABEL(peak_label[IDX_RMIX]), + peak_level_to_db(peak2_level)); /* put new value in label */ + if (peak2_level >= 255) { /* if at 0dB, make label red; RESET reverts to normal color */ + gdk_color_parse("red", &color); + gtk_widget_modify_fg(peak_label[IDX_RMIX], GTK_STATE_NORMAL, &color); + } + } +} + +/* + * NPM: Called by redraw_meters(), this is the normal case + * where we draw the meter and peaks, if changed. + */ +static void draw_meters_and_peaks(int idx, int width, int height, int level1, int level2, + int stereo, int segment_width) { + int meter1 = (stereo) + ? (int) ( (((double)peak_levels[IDX_LMIX]) + / ((double)NUM_METERING_LEVELS)) + * ((double)height)) + : (int) ( (((double)peak_levels[idx - 1]) + / ((double)NUM_METERING_LEVELS)) + * ((double)height)); + int meter2 = (stereo) + ? (int) ( (((double)peak_levels[IDX_RMIX]) + / ((double)NUM_METERING_LEVELS)) + * ((double)height)) + : 0; + int peak1 = height - meter1; + int peak2 = (stereo) ? height - meter2: 0; + + /* + * draw the peak(s), only if changed + */ + if ( (stereo && (peak_changed[IDX_LMIX] || peak_changed[IDX_RMIX])) + || peak_changed[idx-1]) { + int peak2_level, peak1_level + = (stereo) ? peak_levels[IDX_LMIX] : peak_levels[idx-1]; + + /* Draw the peak as a single line */ + gdk_draw_line(pixmap[idx], + GET_COLOR_FOR_PEAKLEVEL(peak1_level), + //X1 + 6, + //Y1 + peak1 - 1, + //X2 + 5 + segment_width, + //Y2 + peak1 - 1 + ); + /* Handle special "stereo" case for right-channel of digital mixer */ + if (stereo && peak_changed[IDX_RMIX]) { //for stereo, draw RMIX, but skip redraw if same + peak2_level = peak_levels[IDX_RMIX]; + gdk_draw_line(pixmap[idx], + GET_COLOR_FOR_PEAKLEVEL(peak2_level), + //X1 + 2 + (width / 2), + //Y1 + peak2 - 1, + //X2 + 1 + (width / 2) + segment_width, + //Y2 + peak2 - 1 + ); + } + else + peak2_level = -1; /* not used unless above case, which is also in draw_peak_labels()... but initialize anyways */ + + draw_peak_labels(idx-1, stereo, peak1_level, peak2_level); + + /* reset peak_changed[] status now that new peak values rendered */ + if (stereo) { + if (peak_changed[IDX_LMIX]) + peak_changed[IDX_LMIX] = FALSE; + if (peak_changed[IDX_RMIX]) + peak_changed[IDX_RMIX] = FALSE; + } + else { + peak_changed[idx-1] = FALSE; + } + } + + /* + * draw the meters + * level1 at 0 --> no dspl + * level1 at 1 --> draw rectange at (1/255)*height + * level1 at 255 --> draw rectangle (255/255)*height + */ + meter1 = (int) ((((double)level1) + / ((double)NUM_METERING_LEVELS)) + * (double)height); + if (stereo) + meter2 = (int) ((((double)level2) + / ((double)NUM_METERING_LEVELS)) + * (double)height); + if (level1 != (stereo ? previous_levels[IDX_LMIX] : previous_levels[idx-1]) ) { //skip redraw if same + gdk_draw_rectangle(pixmap[idx], + penBackground[idx], + TRUE, + //X + 6, // draw black downward from peak + //Y + peak1, + //WIDTH + segment_width, + //HEIGHT + height - meter1 - peak1 + ); + gdk_draw_rectangle(pixmap[idx], + penGreenLight[idx], + TRUE, + //X + 6, + //Y + height - meter1, + //WIDTH + segment_width, + //HEIGHT + meter1 + ); + /* save current level value, skip redraw next time if no change */ + if (stereo) + previous_levels[IDX_LMIX] = level1; + else + previous_levels[idx-1] = level1; + } + if (stereo && (level2 != previous_levels[IDX_RMIX])) { //for stereo, draw RMIX, but skip redraw if same + gdk_draw_rectangle(pixmap[idx], + penBackground[idx], + TRUE, + //X + 2 + (width / 2), + //Y + peak2, + //WIDTH + segment_width, + //HEIGHT + height - meter2 - peak2 + ); + gdk_draw_rectangle(pixmap[idx], + penOrangeLight[idx], + TRUE, + //X + 2 + (width / 2), + //Y + height - meter2, + //WIDTH + segment_width, + //HEIGHT + meter2 + ); + /* save current level value, skip redraw next time if no change */ + previous_levels[IDX_RMIX] = level2; + } +} + +static void redraw_meters(int idx, int width, int height, int level1, int level2) { + int stereo = (idx == 0); + int segment_width = (stereo) + ? (width / 2) - 8 + : width - 12; + + if ( (stereo && ((peak_changed[IDX_LMIX] == RESET) || (peak_changed[IDX_RMIX] == RESET))) + || peak_changed[idx-1] == RESET) //needs full refresh, reset peaks button was clicked + draw_meters_reset(idx, width, height, level1, level2, stereo, segment_width); + else + draw_meters_and_peaks(idx, width, height, level1, level2, stereo, segment_width); +} + +gint level_meters_configure_event(GtkWidget *widget, GdkEventConfigure *event) { int idx = get_index(gtk_widget_get_name(widget)); if (pixmap[idx] != NULL) @@ -158,11 +417,10 @@ gint level_meters_configure_event(GtkWidget *widget, GdkEventConfigure *event) widget->allocation.width, widget->allocation.height, -1); - penGreenShadow[idx] = get_pen(idx, 0, 0x77ff, 0); + penWhiteLight[idx] = get_pen(idx, 0xffff, 0xffff, 0xffff); penGreenLight[idx] = get_pen(idx, 0, 0xffff, 0); - penOrangeShadow[idx] = get_pen(idx, 0xddff, 0x55ff, 0); - penOrangeLight[idx] = get_pen(idx, 0xffff, 0x99ff, 0); - penRedShadow[idx] = get_pen(idx, 0xaaff, 0, 0); + penBackground[idx] = get_pen(idx, 0x1111, 0x5511, 0); + penOrangeLight[idx] = get_pen(idx, 0xff11, 0x9911, 0); penRedLight[idx] = get_pen(idx, 0xffff, 0, 0); gdk_draw_rectangle(pixmap[idx], widget->style->black_gc, @@ -170,13 +428,30 @@ gint level_meters_configure_event(GtkWidget *widget, GdkEventConfigure *event) 0, 0, widget->allocation.width, widget->allocation.height); + + /* NPM: ensure redraw_meters() below does a full refresh, per meter */ + if (idx == 0) { /* "if stereo" -- special case for L/R output pair of digital mixer */ + // g_print("level_meters_configure_event() for stereo\n"); + peak_levels[IDX_LMIX] = 0; + peak_levels[IDX_RMIX] = 0; + previous_levels[IDX_LMIX] = 0; + previous_levels[IDX_RMIX] = 0; + peak_changed[IDX_LMIX] = RESET; + peak_changed[IDX_RMIX] = RESET; + } + else { + // g_print("level_meters_configure_event() for %i\n", idx); + peak_levels[idx-1] = 0; + previous_levels[idx-1] = 0; + peak_changed[idx-1] = RESET; + } + // g_print("configure: %i:%i\n", widget->allocation.width, widget->allocation.height); redraw_meters(idx, widget->allocation.width, widget->allocation.height, 0, 0); return TRUE; } -gint level_meters_expose_event(GtkWidget *widget, GdkEventExpose *event) -{ +gint level_meters_expose_event(GtkWidget *widget, GdkEventExpose *event) { int idx = get_index(gtk_widget_get_name(widget)); int l1, l2; @@ -191,8 +466,7 @@ gint level_meters_expose_event(GtkWidget *widget, GdkEventExpose *event) return FALSE; } -gint level_meters_timeout_callback(gpointer data) -{ +gint level_meters_timeout_callback(gpointer data) { GtkWidget *widget; int idx, l1, l2; @@ -209,6 +483,48 @@ gint level_meters_timeout_callback(gpointer data) 0, 0, widget->allocation.width, widget->allocation.height); } + /* NPM: both cases below are special-case hack to get + "Analog Volume" PCM peak levels updating correctly, + should "Analog Volume" panel be selected before "Monitor + PCM outs" panel. In that situation, + "(GTK_WIDGET_VISIBLE(widget) && (pixmap[idx] != NULL))" + fails as level_meters_configure_event() hasn't been + called yet (it gets called when user selects "Monitor + PCM outs" panel). + */ + else if ((idx != 0) + && ((idx-1) < envy_dac_volumes()) + && (peak_changed[idx-1] == TRUE)) { + draw_peak_labels(idx-1, FALSE, l1, l2); + peak_changed[idx-1] = FALSE; + // g_print("NPM PCM outputs hack calling draw_peak_labels(%i): %i\n", idx-1, l1); + } + /* NPM: make it work, even after user does a RESET, while + in "Analog Volume" panel, before "Monitor PCM outs" + created/configured */ + else if ((idx != 0) + && ((idx-1) < envy_dac_volumes()) + && (peak_changed[idx-1] == RESET)) { /* note that get_levels() never retrieved l1, l2 due to unfulfilled RESET */ + GdkColor color; + GtkWidget* lbl; + gdk_color_parse("black", &color); + /* NPM: Reset colors of peak labels for DACs in "Analog Volume" panel */ + if ((idx-1) < envy_dac_volumes() /* index 0-8 corresponds to one of the eight DAC's */ + && ((idx-1) < MAX_OUTPUT_CHANNELS) /* make sure within bounds of dac_peak_label[] */ + && (lbl = dac_peak_label[idx-1]) != NULL) + gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color); /* reset color */ + /* NPM: Reset colors of peak labels for ADCs in "Analog Volume" panel */ + else if (((idx-1) >= (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) /* ADC channels begin at 11 = MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS */ + && ((idx-1) < (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS + envy_adc_volumes())) /* ADC channels end at 19 */ + && (((idx-1) - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)) < MAX_INPUT_CHANNELS) /* make sure within bounds of adc_peak_label[] */ + && (lbl = adc_peak_label[(idx-1) - (MAX_PCM_OUTPUT_CHANNELS+MAX_SPDIF_CHANNELS)]) != NULL) + gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color); /* reset color */ + + peak_changed[idx-1] = FALSE; /* hack -- force get_levels() to retrieve levels despite RESET */ + get_levels(idx, &l1, &l2); + draw_peak_labels(idx-1, FALSE, l1, l2); + // g_print("NPM PCM outputs RESET hack calling draw_peak_labels(%i): %i\n", idx-1, l1); + } } if (view_spdif_playback) { for (idx = MAX_PCM_OUTPUT_CHANNELS + 1; idx <= MAX_OUTPUT_CHANNELS + spdif_channels; idx++) { @@ -255,12 +571,20 @@ gint level_meters_timeout_callback(gpointer data) return TRUE; } -void level_meters_reset_peaks(GtkButton *button, gpointer data) -{ + +/* NPM fixed lack of implementation ( https://bugzilla.redhat.com/show_bug.cgi?id=602903 )*/ +void level_meters_reset_peaks(GtkButton *button, gpointer data) { + int i; + for (i = 0; i < MULTI_TRACK_PEAK_CHANNELS ; i++) { + peak_levels[i] = 0; + previous_levels[i] = 0; + peak_changed[i] = RESET; + } + + level_meters_timeout_callback((gpointer) data); } -void level_meters_init(void) -{ +void level_meters_init(void) { int err; snd_ctl_elem_value_malloc(&peaks); @@ -272,7 +596,6 @@ void level_meters_init(void) SND_CTL_ELEM_IFACE_MIXER); } -void level_meters_postinit(void) -{ +void level_meters_postinit(void) { level_meters_timeout_callback(NULL); } diff --git a/envy24control/midi.c b/envy24control/midi.c index 327f6cf..774a2a1 100644 --- a/envy24control/midi.c +++ b/envy24control/midi.c @@ -23,6 +23,7 @@ #include "midi.h" #include #include +#include "envy24control.h" static const int midi2slider_lin[128] = { 96, 96, 95, 94, 93, 93, 92, 91, @@ -149,7 +150,7 @@ int midi_controller(int c, int v) if(c<0 || c>127) return 0; if(v<0) v=0; - else if(v>96) v=96; + else if(v>MAX_MIXER_ATTENUATION_VALUE) v=MAX_MIXER_ATTENUATION_VALUE; v2=slider2midi[v]; #if 0 fprintf(stderr, "midi_controller(%i,%i)->%i\n",c,v,v2); @@ -248,7 +249,7 @@ void midi_process(gpointer data, gint source, GdkInputCondition condition) snd_seq_event_t *ev; static GtkAdjustment *adj=0; if(!adj) - adj=(GtkAdjustment*) gtk_adjustment_new(0, 0, 96, 1, 1, 10); + adj=(GtkAdjustment*) gtk_adjustment_new(0, 0, MAX_MIXER_ATTENUATION_VALUE, 1, 1, 10); do { @@ -332,7 +333,7 @@ int main() cout << "static const int midi2slider_enh[128] = {" << endl; for(i=0; i<128; ++i) { - int v=96-midi2slider[i]; + int v=MAX_MIXER_ATTENUATION_VALUE-midi2slider[i]; cout << v << ", "; if(((++z)%8)==7) cout << endl; @@ -344,7 +345,7 @@ int main() vector rm; cout << "static const int slider2midi_enh[97] = {" << endl; int last=0; - for(i=0; i<97; ++i) + for(i=0; i=0; --i) + for(i=MAX_MIXER_ATTENUATION_VALUE; i>=0; --i) { cout << rm[i] << ", "; if(((++z)%8)==7) diff --git a/envy24control/mixer.c b/envy24control/mixer.c index f2b4e9a..8db7fa2 100644 --- a/envy24control/mixer.c +++ b/envy24control/mixer.c @@ -62,8 +62,8 @@ void mixer_update_stream(int stream, int vol_flag, int sw_flag) v[1] = snd_ctl_elem_value_get_integer(vol, 1); if (v[0] != v[1]) toggle_set(mixer_stereo_toggle[stream-1], FALSE); - gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][0]), 96 - v[0]); - gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][1]), 96 - v[1]); + gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][0]), MAX_MIXER_ATTENUATION_VALUE - v[0]); + gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][1]), MAX_MIXER_ATTENUATION_VALUE - v[1]); midi_controller((stream-1)*2, v[0]); midi_controller((stream-1)*2+1, v[1]); } @@ -181,11 +181,17 @@ void mixer_adjust(GtkAdjustment *adj, gpointer data) int stereo = is_active(mixer_stereo_toggle[stream-1]) ? 1 : 0; int vol[2] = { -1, -1 }; - vol[button] = 96 - adj->value; + vol[button] = MAX_MIXER_ATTENUATION_VALUE - adj->value; if (stereo) { gtk_adjustment_set_value(GTK_ADJUSTMENT(mixer_adj[stream-1][button ^ 1]), adj->value); - vol[button ^ 1] = 96 - adj->value; + vol[button ^ 1] = MAX_MIXER_ATTENUATION_VALUE - adj->value; } + if (vol[0] != -1) + gtk_label_set_text(GTK_LABEL(mixer_label[stream-1][0]), + (gchar*)mixer_volume_to_db(vol[0])); + if (vol[1] != -1) + gtk_label_set_text(GTK_LABEL(mixer_label[stream-1][1]), + (gchar*)mixer_volume_to_db(vol[1])); set_volume1(stream, vol[0], vol[1]); } diff --git a/envy24control/volume.c b/envy24control/volume.c index 4386e35..2b3b9c3 100644 --- a/envy24control/volume.c +++ b/envy24control/volume.c @@ -20,6 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "envy24control.h" #define toggle_set(widget, state) \ @@ -213,20 +214,220 @@ void adc_sense_update(int idx) /* */ +/* + NPM: Use dB values like given by output of "amixer", for the ice1712 DAC: + 0 --> "Mono: 0 [0%] [-99999.99dB]" + 1 --> "Mono: 1 [1%] [-63.00dB]" + 2 --> "Mono: 2 [2%] [-62.50dB]" + ... + 96--> "Mono: 127 [100%] [0.00dB]" + */ +static char temp_label[16]; +char* dac_volume_to_db(int ival) { + if (ival != 0) { + float val = ((float)ival * 0.5) - 63.5; /* step size is 0.5dB */ + if (val <= -10) { + sprintf(temp_label, "%+2.1f", val); + } + else { + sprintf(temp_label, " %+2.1f", val); + } + return (temp_label) ; + } + else { + sprintf(temp_label, "%s", "(Off)"); + return (temp_label); + } +} +/* + NPM: Use dB values like given by output of "amixer", for the ice1712 DAC: + 0 --> "Mono: 0 [0%] [-99999.99dB]" + 1 --> "Mono: 1 [1%] [-63.00dB]" + 2 --> "Mono: 2 [1%] [-62.50dB]" + ... + 127 --> "Mono: 127 [78%] [0.00dB]" + ... + 163--> "Mono: 163 [100%] [18.00dB]" + */ +char* adc_volume_to_db(int ival) { + if (ival != 0) { + float val = ((float)(ival - 127)) * 0.5; /* offset so that 127 is 0dB, step size is 1.5dB */ + if (val >= 10) { + sprintf(temp_label, "%+2.1f", val); + } + else if (val >= 0) { + sprintf(temp_label, " %+2.1f", val); + } + else if (val <= -10) { + sprintf(temp_label, "%+2.1f", val); + } + else { + sprintf(temp_label, " %+2.1f", val); + } + return (temp_label) ; + } + else { + sprintf(temp_label, "%s", "(Off)"); + return (temp_label); + } +} + +char* mixer_volume_to_db(int ival) { + if (ival != 0) { + float val = ((float)ival * 1.5) - MAX_MIXER_ATTENUATION_DB; /* step size is 1.5dB */ + if (val <= -10) { + sprintf(temp_label, "%+2.1f", val); + } + else { + sprintf(temp_label, " %+2.1f", val); + } + return (temp_label) ; + } + else { + sprintf(temp_label, "%s", "(Off)"); + return (temp_label); + } +} + +/* + * NPM: Per Fons Adriaensen''s message to linux-audio-user's list + * July 2010 ( http://www.linuxaudio.org/mailarchive/lad/2010/7/13/171540 ) + * "L dB (L < 0) corresponds to 255 * (10 ^ L / 20)" + * Alternately: http://en.wikipedia.org/wiki/Decibel#Field_quantities + * 20 log10(V1/V0) where V1 is voltage measured and V0 is reference. + * ... of course, the value of the digital peak meters isn't the "voltage" + * for example an output of 1/255 --> −48.130803609dB + * whereas 255/255 --> 0dB + * 254/255 --> −0.034129276 + * 250/255 --> −0.172003435 + * http://alsa.cybermirror.org/manuals/icensemble/envy24.pdf states + * "Peak data derived from the absolute value of 9 msb. + * 00h min - FFh max volume. Reading the register + * resets the meter to 00h." + */ +char* peak_level_to_db(int ival) { + if (ival != 0) { + double value = 20.0 * log10((double)ival/(double)255); + // "(Off)" + // "0dBFS" + // "-0.10" + // "-9.90" + // "-48.0" + if (value == 0.0) + sprintf(temp_label, "0dBFS"); + else if (value > -10.0) + sprintf(temp_label, "%+1.2f", value); + else + sprintf(temp_label, "%+2.1f", value); + return (temp_label); + } + else + return ("(Off)"); +} + +/* + * NPM: "subroutine" used twice in create_mixer_frame() to draw markings + * for each digital mixer input attenuation slider. Each slider is drawn + * optionally with or without a legend describing the dBs attenuation at + * the given level. Each slider controls attentuation of its input from + * +0dB to -144.0dB (and "Off"). + */ +void draw_24bit_attenuator_scale_markings(GtkScale *scale, GtkPositionType position, int draw_legend_p) { + gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE, + position, draw_legend_p ? "+0": NULL); + gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+1*ANALOG_GAIN_STEP_SIZE, + position, draw_legend_p ? "-18" : NULL); + gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+2*ANALOG_GAIN_STEP_SIZE, + position, draw_legend_p ? "-36" : NULL); + gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+3*ANALOG_GAIN_STEP_SIZE, + position, draw_legend_p ? "-54" : NULL); + gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+4*ANALOG_GAIN_STEP_SIZE, + position, draw_legend_p ? "-72" : NULL); + gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+5*ANALOG_GAIN_STEP_SIZE, + position, draw_legend_p ? "-90" : NULL); + gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+6*ANALOG_GAIN_STEP_SIZE, + position, draw_legend_p ? "-108" : NULL); + gtk_scale_add_mark(scale, (float) MIN_MIXER_ATTENUATION_VALUE+7*ANALOG_GAIN_STEP_SIZE, + position, draw_legend_p ? "-126" : NULL); + gtk_scale_add_mark(scale, (float) MAX_MIXER_ATTENUATION_VALUE, + position, NULL); /* NPM: last marker needs to not be a label, else the last level gets placed incorrectly (gtk2-2.18.9-3.fc12.x86_64 bug?) */ +} + +/* NPM: used in create_analog_volume() to draw dB markings on DAC attenuators */ +void draw_dac_scale_markings(GtkScale *scale, GtkPositionType position) { + gtk_scale_add_mark(scale, (float) -envy_dac_max(), + position, "+0"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+1*ANALOG_GAIN_STEP_SIZE, + position, "-6"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+2*ANALOG_GAIN_STEP_SIZE, + position, "-12"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+3*ANALOG_GAIN_STEP_SIZE, + position, "-18"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+4*ANALOG_GAIN_STEP_SIZE, + position, "-24"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+5*ANALOG_GAIN_STEP_SIZE, + position, "-30"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+6*ANALOG_GAIN_STEP_SIZE, + position, "-36"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+7*ANALOG_GAIN_STEP_SIZE, + position, "-42"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+8*ANALOG_GAIN_STEP_SIZE, + position, "-48"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+9*ANALOG_GAIN_STEP_SIZE, + position, "-54"); + gtk_scale_add_mark(scale, (float) -envy_dac_max()+10*ANALOG_GAIN_STEP_SIZE, + position, "-60"); + gtk_scale_add_mark(scale, (float) 0, + position, NULL); /* NPM: last marker needs to not be a label, else the last level gets placed incorrectly (gtk2-2.18.9-3.fc12.x86_64 bug?) */ +} + +/* NPM: used in create_analog_volume() to draw dB markings on ADC attenuators/amplifiers */ +void draw_adc_scale_markings(GtkScale *scale, GtkPositionType position) { + gtk_scale_add_mark(scale, (float) -envy_adc_max(), + position, "+18"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+1*ANALOG_GAIN_STEP_SIZE, + position, "+12"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+2*ANALOG_GAIN_STEP_SIZE, + position, "+6"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+3*ANALOG_GAIN_STEP_SIZE, + position, "+0"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+4*ANALOG_GAIN_STEP_SIZE, + position, "-6"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+5*ANALOG_GAIN_STEP_SIZE, + position, "-12"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+6*ANALOG_GAIN_STEP_SIZE, + position, "-18"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+7*ANALOG_GAIN_STEP_SIZE, + position, "-24"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+8*ANALOG_GAIN_STEP_SIZE, + position, "-30"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+9*ANALOG_GAIN_STEP_SIZE, + position, "-36"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+10*ANALOG_GAIN_STEP_SIZE, + position, "-42"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+11*ANALOG_GAIN_STEP_SIZE, + position, "-48"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+12*ANALOG_GAIN_STEP_SIZE, + position, "-54"); + gtk_scale_add_mark(scale, (float) -envy_adc_max()+13*ANALOG_GAIN_STEP_SIZE, + position, "-60"); + gtk_scale_add_mark(scale, (float) 0, + position, NULL); /* NPM: last marker needs to not be a label, else the last level gets placed incorrectly (gtk2-2.18.9-3.fc12.x86_64 bug?) */ +} + void dac_volume_adjust(GtkAdjustment *adj, gpointer data) { int idx = (int)(long)data; snd_ctl_elem_value_t *val; int err, ival = -(int)adj->value; - char text[16]; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, DAC_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); snd_ctl_elem_value_set_integer(val, 0, ival); - sprintf(text, "%03i", ival); - gtk_label_set_text(av_dac_volume_label[idx], text); + gtk_label_set_text(GTK_LABEL(av_dac_volume_label[idx]), + dac_volume_to_db(ival)); /* NPM: changed to output dB values */ if ((err = snd_ctl_elem_write(ctl, val)) < 0) g_print("Unable to write dac volume: %s\n", snd_strerror(err)); } @@ -236,15 +437,14 @@ void adc_volume_adjust(GtkAdjustment *adj, gpointer data) int idx = (int)(long)data; snd_ctl_elem_value_t *val; int err, ival = -(int)adj->value; - char text[16]; snd_ctl_elem_value_alloca(&val); snd_ctl_elem_value_set_interface(val, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(val, ADC_VOLUME_NAME); snd_ctl_elem_value_set_index(val, idx); snd_ctl_elem_value_set_integer(val, 0, ival); - sprintf(text, "%03i", ival); - gtk_label_set_text(av_adc_volume_label[idx], text); + gtk_label_set_text(GTK_LABEL(av_adc_volume_label[idx]), + adc_volume_to_db(ival)); /* NPM: changed to output dB values */ if ((err = snd_ctl_elem_write(ctl, val)) < 0) g_print("Unable to write adc volume: %s\n", snd_strerror(err)); } @@ -262,7 +462,7 @@ void ipga_volume_adjust(GtkAdjustment *adj, gpointer data) snd_ctl_elem_value_set_index(val, idx); snd_ctl_elem_value_set_integer(val, 0, ival); sprintf(text, "%03i", ival); - gtk_label_set_text(av_ipga_volume_label[idx], text); + gtk_label_set_text(GTK_LABEL(av_ipga_volume_label[idx]), text); if ((err = snd_ctl_elem_write(ctl, val)) < 0) g_print("Unable to write ipga volume: %s\n", snd_strerror(err)); }