Add support for Level Meter labels
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-FileCopyrightText: 2022-2024 Geoffrey D. Bennett <g@b4.vu>
|
// SPDX-FileCopyrightText: 2022-2024 Geoffrey D. Bennett <g@b4.vu>
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
#include "gtkdial.h"
|
#include "gtkdial.h"
|
||||||
@@ -26,6 +27,8 @@ static const double level_colours[] = {
|
|||||||
struct levels {
|
struct levels {
|
||||||
struct alsa_card *card;
|
struct alsa_card *card;
|
||||||
struct alsa_elem *level_meter_elem;
|
struct alsa_elem *level_meter_elem;
|
||||||
|
GtkWidget *top;
|
||||||
|
GtkGrid *grid;
|
||||||
GtkWidget *meters[MAX_METERS];
|
GtkWidget *meters[MAX_METERS];
|
||||||
guint timer;
|
guint timer;
|
||||||
};
|
};
|
||||||
@@ -67,21 +70,121 @@ static void on_destroy(struct levels *data, GtkWidget *widget) {
|
|||||||
g_free(data);
|
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) {
|
GtkWidget *create_levels_controls(struct alsa_card *card) {
|
||||||
struct levels *data = g_malloc0(sizeof(struct levels));
|
struct levels *data = g_malloc0(sizeof(struct levels));
|
||||||
|
|
||||||
data->card = card;
|
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");
|
gtk_widget_add_css_class(top, "window-frame");
|
||||||
|
|
||||||
GtkWidget *levels_top = gtk_grid_new();
|
GtkWidget *grid_widget = gtk_grid_new();
|
||||||
gtk_widget_add_css_class(levels_top, "window-content");
|
gtk_widget_add_css_class(grid_widget, "window-content");
|
||||||
gtk_widget_add_css_class(levels_top, "top-level-content");
|
gtk_widget_add_css_class(grid_widget, "top-level-content");
|
||||||
gtk_widget_add_css_class(levels_top, "window-levels");
|
gtk_widget_add_css_class(grid_widget, "window-levels");
|
||||||
gtk_frame_set_child(GTK_FRAME(top), levels_top);
|
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 };
|
GtkWidget *count_labels[MAX_MUX_IN] = { NULL };
|
||||||
|
|
||||||
@@ -93,6 +196,9 @@ GtkWidget *create_levels_controls(struct alsa_card *card) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->level_meter_elem->meter_labels)
|
||||||
|
return create_levels_controls_with_labels(card, data);
|
||||||
|
|
||||||
// go through the port categories
|
// go through the port categories
|
||||||
for (int i = 0, row = 1; i < PC_COUNT; i++) {
|
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;
|
int elem_count = data->level_meter_elem->count;
|
||||||
if (meter_num != elem_count) {
|
if (meter_num != elem_count) {
|
||||||
printf("meter_num is %d but elem count is %d\n", 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->level_meter_elem->count = elem_count;
|
||||||
|
|
||||||
data->timer = g_timeout_add(50, update_levels_controls, data);
|
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;
|
return top;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user