From dc21eb52d05ccc494d50ff202a511e000cd810e0 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Sat, 4 Jan 2025 01:59:17 +1030 Subject: [PATCH] Add support for Level Meter labels --- src/window-levels.c | 123 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 9 deletions(-) diff --git a/src/window-levels.c b/src/window-levels.c index 486249c..c3b4290 100644 --- a/src/window-levels.c +++ b/src/window-levels.c @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022-2024 Geoffrey D. Bennett // SPDX-License-Identifier: GPL-3.0-or-later +#include #include #include "gtkdial.h" @@ -26,6 +27,8 @@ static const double level_colours[] = { struct levels { struct alsa_card *card; struct alsa_elem *level_meter_elem; + GtkWidget *top; + GtkGrid *grid; GtkWidget *meters[MAX_METERS]; guint timer; }; @@ -67,21 +70,121 @@ static void on_destroy(struct levels *data, GtkWidget *widget) { g_free(data); } +static GtkWidget *create_levels_controls_with_labels( + struct alsa_card *card, + struct levels *data +) { + struct alsa_elem *level_meter_elem = data->level_meter_elem; + int count = level_meter_elem->count; + + int row = 1; + int max_count = 0; + char *current_type = NULL; + + for (int meter_num = 0; meter_num < count; meter_num++) { + char *label = strdup(level_meter_elem->meter_labels[meter_num]); + + if (!label) { + fprintf(stderr, "Couldn't strdup label\n"); + exit(1); + } + + if (strlen(label) < 3) { + fprintf(stderr, "Label too short: %s\n", label); + exit(1); + } + + // Label is "Source Analogue 1" or "Source Mix A" or "Sink + // Analogue 1", etc. + // get the number part of the label looking from the end + int label_idx = strlen(label) - 1; + int label_num = 1; + if (isdigit(label[label_idx])) { + while (label_idx > 1 && isdigit(label[label_idx - 1])) + label_idx--; + label_num = atoi(&label[label_idx]); + + if (label[label_idx - 1] != ' ') { + fprintf(stderr, "Label %s is not in the expected format\n", label); + exit(1); + } + label[label_idx - 1] = '\0'; + + } else if (label[label_idx] >= 'A' && label[label_idx] <= 'Z') { + label_num = label[label_idx] - 'A' + 1; + + if (label[label_idx - 1] != ' ') { + fprintf(stderr, "Label %s is not in the expected format\n", label); + exit(1); + } + + label[label_idx - 1] = '\0'; + } + + if (label_num > max_count) + max_count = label_num; + + if (!current_type || strcmp(current_type, label)) { + row++; + + free(current_type); + current_type = strdup(label); + + GtkWidget *l = gtk_label_new(current_type); + gtk_widget_set_halign(l, GTK_ALIGN_END); + + // add the type label + gtk_grid_attach(GTK_GRID(data->grid), l, 0, row, 1, 1); + } + + GtkWidget *meter = gtk_dial_new_with_range(-80, 0, 0, 0); + gtk_dial_set_taper(GTK_DIAL(meter), GTK_DIAL_TAPER_LINEAR); + gtk_dial_set_can_control(GTK_DIAL(meter), FALSE); + gtk_dial_set_peak_hold(GTK_DIAL(meter), 1000); + gtk_dial_set_level_meter_colours( + GTK_DIAL(meter), + level_breakpoints_out, + level_colours, + sizeof(level_breakpoints_out) / sizeof(int) + ); + gtk_widget_set_sensitive(meter, FALSE); + gtk_dial_set_off_db(GTK_DIAL(meter), -45); + gtk_grid_attach(GTK_GRID(data->grid), meter, label_num, row, 1, 1); + data->meters[meter_num] = meter; + + free(label); + } + + free(current_type); + + for (int col = 1; col <= max_count; col++) { + char s[20]; + sprintf(s, "%d", col); + GtkWidget *l = gtk_label_new(s); + gtk_grid_attach(GTK_GRID(data->grid), l, col, 0, 1, 1); + } + + data->timer = g_timeout_add(50, update_levels_controls, data); + g_object_weak_ref(G_OBJECT(data->grid), (GWeakNotify)on_destroy, data); + + return data->top; +} + GtkWidget *create_levels_controls(struct alsa_card *card) { struct levels *data = g_malloc0(sizeof(struct levels)); data->card = card; - GtkWidget *top = gtk_frame_new(NULL); + GtkWidget *top = data->top = gtk_frame_new(NULL); gtk_widget_add_css_class(top, "window-frame"); - GtkWidget *levels_top = gtk_grid_new(); - gtk_widget_add_css_class(levels_top, "window-content"); - gtk_widget_add_css_class(levels_top, "top-level-content"); - gtk_widget_add_css_class(levels_top, "window-levels"); - gtk_frame_set_child(GTK_FRAME(top), levels_top); + GtkWidget *grid_widget = gtk_grid_new(); + gtk_widget_add_css_class(grid_widget, "window-content"); + gtk_widget_add_css_class(grid_widget, "top-level-content"); + gtk_widget_add_css_class(grid_widget, "window-levels"); + gtk_frame_set_child(GTK_FRAME(top), grid_widget); - GtkGrid *grid = GTK_GRID(levels_top); + GtkGrid *grid = data->grid = GTK_GRID(grid_widget); GtkWidget *count_labels[MAX_MUX_IN] = { NULL }; @@ -93,6 +196,9 @@ GtkWidget *create_levels_controls(struct alsa_card *card) { return NULL; } + if (data->level_meter_elem->meter_labels) + return create_levels_controls_with_labels(card, data); + // go through the port categories for (int i = 0, row = 1; i < PC_COUNT; i++) { @@ -140,12 +246,11 @@ GtkWidget *create_levels_controls(struct alsa_card *card) { int elem_count = data->level_meter_elem->count; if (meter_num != elem_count) { printf("meter_num is %d but elem count is %d\n", meter_num, elem_count); - return NULL; } data->level_meter_elem->count = elem_count; data->timer = g_timeout_add(50, update_levels_controls, data); - g_object_weak_ref(G_OBJECT(levels_top), (GWeakNotify)on_destroy, data); + g_object_weak_ref(G_OBJECT(grid), (GWeakNotify)on_destroy, data); return top; }