Add support for Level Meter labels

This commit is contained in:
Geoffrey D. Bennett
2025-01-04 01:59:17 +10:30
parent c4ab20f9b5
commit dc21eb52d0

View File

@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022-2024 Geoffrey D. Bennett <g@b4.vu>
// SPDX-License-Identifier: GPL-3.0-or-later
#include <ctype.h>
#include <gtk/gtk.h>
#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;
}