From be458afcc4f48ade8bd95ca1135854bd20c7fe4c Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Thu, 30 Nov 2023 23:57:47 +1030 Subject: [PATCH] Remove hard-coded values from widget-gain.c and widget-volume.c Update alsa-sim.c and alsa.c to read the TLV info, and update the gain and volume widgets to use that info rather than hard-coding the min/max values. --- src/alsa-sim.c | 36 ++++++++++++++++++++++++++++++++ src/alsa.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ src/alsa.h | 6 ++++++ src/widget-gain.c | 20 ++++++++++-------- src/widget-volume.c | 20 ++++++++++-------- 5 files changed, 116 insertions(+), 16 deletions(-) diff --git a/src/alsa-sim.c b/src/alsa-sim.c index f9f65f5..5ba0be5 100644 --- a/src/alsa-sim.c +++ b/src/alsa-sim.c @@ -151,6 +151,42 @@ static void alsa_parse_comment_node( elem->type = SND_CTL_ELEM_TYPE_INTEGER; } else if (strcmp(key, "item") == 0) { alsa_parse_enum_items(node, elem); + } else if (strcmp(key, "range") == 0) { + if (type != SND_CONFIG_TYPE_STRING) { + printf("range type not string\n"); + return; + } + const char *range; + err = snd_config_get_string(node, &range); + if (err < 0) + fatal_alsa_error("snd_config_get_string error", err); + + // Parse the range string and update elem->min_val and elem->max_val + int min_val, max_val; + if (sscanf(range, "%d - %d", &min_val, &max_val) == 2) { + elem->min_val = min_val; + elem->max_val = max_val; + } + } else if (strcmp(key, "dbmin") == 0) { + if (type != SND_CONFIG_TYPE_INTEGER) { + printf("dbmin type not integer\n"); + return; + } + long dbmin; + err = snd_config_get_integer(node, &dbmin); + if (err < 0) + fatal_alsa_error("snd_config_get_integer error", err); + elem->min_dB = dbmin / 100; + } else if (strcmp(key, "dbmax") == 0) { + if (type != SND_CONFIG_TYPE_INTEGER) { + printf("dbmax type not integer\n"); + return; + } + long dbmax; + err = snd_config_get_integer(node, &dbmax); + if (err < 0) + fatal_alsa_error("snd_config_get_integer error", err); + elem->max_dB = dbmax / 100; } } } diff --git a/src/alsa.c b/src/alsa.c index 7ea8ac6..6b55dfb 100644 --- a/src/alsa.c +++ b/src/alsa.c @@ -7,6 +7,8 @@ #include "stringhelper.h" #include "window-iface.h" +#define MAX_TLV_RANGE_SIZE 256 + // names for the port categories const char *port_category_names[PC_COUNT] = { "Hardware Outputs", @@ -340,6 +342,54 @@ static void alsa_get_elem_list(struct alsa_card *card) { if (strstr(alsa_elem.name, "Channel Map")) continue; + // get TLV info if it's a volume control + if (alsa_elem.type == SND_CTL_ELEM_TYPE_INTEGER) { + snd_ctl_elem_info_t *elem_info; + + snd_ctl_elem_info_alloca(&elem_info); + snd_ctl_elem_info_set_numid(elem_info, alsa_elem.numid); + snd_ctl_elem_info(card->handle, elem_info); + + if (snd_ctl_elem_info_is_tlv_readable(elem_info)) { + snd_ctl_elem_id_t *elem_id; + unsigned int tlv[MAX_TLV_RANGE_SIZE]; + unsigned int *dbrec; + int ret; + long min_dB, max_dB; + + snd_ctl_elem_id_alloca(&elem_id); + snd_ctl_elem_id_set_numid(elem_id, alsa_elem.numid); + + ret = snd_ctl_elem_tlv_read( + card->handle, elem_id, tlv, sizeof(tlv) + ); + if (ret < 0) { + fprintf(stderr, "TLV read error %d\n", ret); + continue; + } + + ret = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &dbrec); + if (ret <= 0) { + fprintf(stderr, "TLV parse error %d\n", ret); + continue; + } + + int min_val = snd_ctl_elem_info_get_min(elem_info); + int max_val = snd_ctl_elem_info_get_max(elem_info); + + ret = snd_tlv_get_dB_range(tlv, min_val, max_val, &min_dB, &max_dB); + if (ret != 0) { + fprintf(stderr, "TLV range error %d\n", ret); + continue; + } + + alsa_elem.min_val = min_val; + alsa_elem.max_val = max_val; + alsa_elem.min_dB = min_dB / 100; + alsa_elem.max_dB = max_dB / 100; + } + } + if (card->elems->len <= alsa_elem.numid) g_array_set_size(card->elems, alsa_elem.numid + 1); g_array_index(card->elems, struct alsa_elem, alsa_elem.numid) = alsa_elem; diff --git a/src/alsa.h b/src/alsa.h index bd08d4d..2013d29 100644 --- a/src/alsa.h +++ b/src/alsa.h @@ -115,6 +115,12 @@ struct alsa_elem { int type; int count; + // for gain/volume elements, the dB range and step + int min_val; + int max_val; + int min_dB; + int max_dB; + // for the number (or translated letter; A = 1) in the item name // TODO: move this to struct routing_snk? int lr_num; diff --git a/src/widget-gain.c b/src/widget-gain.c index 1e76ace..ed322e0 100644 --- a/src/widget-gain.c +++ b/src/widget-gain.c @@ -4,11 +4,6 @@ #include "gtkdial.h" #include "widget-gain.h" -// gain controls -80dB - +6dB, 0.5dB steps -#define DIAL_MIN_VALUE 0 -#define DIAL_MAX_VALUE 172 -#define DIAL_ZERO_DB_VALUE 160 - static void gain_changed(GtkWidget *widget, struct alsa_elem *elem) { int value = gtk_dial_get_value(GTK_DIAL(widget)); @@ -23,7 +18,10 @@ static void gain_updated(struct alsa_elem *elem) { gtk_dial_set_value(GTK_DIAL(elem->widget), value); char s[20]; - snprintf(s, 20, "%.1f", (value / 2.0) - 80); + float scale = (float)(elem->max_dB - elem->min_dB) / + (elem->max_val - elem->min_val); + + snprintf(s, 20, "%.1f", value * scale + elem->min_dB); gtk_label_set_text(GTK_LABEL(elem->widget2), s); } @@ -32,9 +30,15 @@ GtkWidget *make_gain_alsa_elem(struct alsa_elem *elem) { gtk_widget_set_hexpand(vbox, TRUE); GtkWidget *dial = gtk_dial_new_with_range( - DIAL_MIN_VALUE, DIAL_MAX_VALUE, 1 + elem->min_val, elem->max_val, 1 ); - gtk_dial_set_zero_db(GTK_DIAL(dial), DIAL_ZERO_DB_VALUE); + + // calculate 0dB value from min/max dB and min/max value + float scale = (float)(elem->max_dB - elem->min_dB) / + (elem->max_val - elem->min_val); + int zero_db_value = (int)((0 - elem->min_dB) / scale + elem->min_val); + + gtk_dial_set_zero_db(GTK_DIAL(dial), zero_db_value); gtk_widget_set_vexpand(dial, TRUE); diff --git a/src/widget-volume.c b/src/widget-volume.c index 6cda54c..61ef001 100644 --- a/src/widget-volume.c +++ b/src/widget-volume.c @@ -4,11 +4,6 @@ #include "gtkdial.h" #include "widget-volume.h" -// volume controls -127dB - 0dB -#define DIAL_MIN_VALUE 0 -#define DIAL_MAX_VALUE 127 -#define DIAL_ZERO_DB_VALUE 127 - static void volume_changed(GtkWidget *widget, struct alsa_elem *elem) { int value = gtk_dial_get_value(GTK_DIAL(widget)); @@ -23,7 +18,10 @@ static void volume_updated(struct alsa_elem *elem) { gtk_dial_set_value(GTK_DIAL(elem->widget), value); char s[20]; - snprintf(s, 20, "%ddB", value - 127); + float scale = (float)(elem->max_dB - elem->min_dB) / + (elem->max_val - elem->min_val); + + snprintf(s, 20, "%ddB", (int)(value * scale + elem->min_dB)); gtk_label_set_text(GTK_LABEL(elem->widget2), s); } @@ -32,9 +30,15 @@ GtkWidget *make_volume_alsa_elem(struct alsa_elem *elem) { gtk_widget_set_hexpand(vbox, TRUE); GtkWidget *dial = gtk_dial_new_with_range( - DIAL_MIN_VALUE, DIAL_MAX_VALUE, 1 + elem->min_val, elem->max_val, 1 ); - gtk_dial_set_zero_db(GTK_DIAL(dial), DIAL_ZERO_DB_VALUE); + + // calculate 0dB value from min/max dB and min/max value + float scale = (float)(elem->max_dB - elem->min_dB) / + (elem->max_val - elem->min_val); + int zero_db_value = (int)((0 - elem->min_dB) / scale + elem->min_val); + + gtk_dial_set_zero_db(GTK_DIAL(dial), zero_db_value); gtk_widget_set_vexpand(dial, TRUE);