Add colour to GtkDial level meters

This commit is contained in:
Geoffrey D. Bennett
2024-02-08 21:30:21 +10:30
parent a26f607a8b
commit 3048b43b9a
3 changed files with 117 additions and 3 deletions

View File

@@ -125,6 +125,11 @@ struct _GtkDial {
double *taper_outputs;
int taper_breakpoints_count;
// level meter colour breakpoints array
const int *level_breakpoints;
const double *level_colours;
int level_breakpoints_count;
// variables derived from the widget's dynamic properties (size and
// configuration, excluding the value)
int dim;
@@ -139,6 +144,7 @@ struct _GtkDial {
double cy;
double zero_db_x;
double zero_db_y;
double *level_breakpoint_angles;
// cairo patterns dependent on the above
cairo_pattern_t *fill_pattern[2][2];
@@ -374,6 +380,21 @@ static void update_dial_properties(GtkDial *dial) {
dial->outline_pattern[dim] = pat;
}
// calculate level meter breakpoint angles
if (dial->level_breakpoint_angles)
free(dial->level_breakpoint_angles);
if (dial->level_breakpoints_count) {
dial->level_breakpoint_angles = malloc(
dial->level_breakpoints_count * sizeof(double)
);
for (int i = 0; i < dial->level_breakpoints_count; i++) {
double valp = calc_taper(dial, dial->level_breakpoints[i]);
dial->level_breakpoint_angles[i] =
calc_val(valp, ANGLE_START, ANGLE_END);
}
}
}
static void update_dial_values(GtkDial *dial) {
@@ -658,10 +679,60 @@ static void draw_slider(
double thickness,
double alpha
) {
cairo_arc(cr, dial->cx, dial->cy, radius, ANGLE_START, dial->angle);
cairo_set_line_width(cr, thickness);
cairo_set_source_rgba_dim(cr, 1, 1, 1, alpha, dial->dim);
cairo_stroke(cr);
int count = dial->level_breakpoints_count;
if (!count) {
cairo_arc(cr, dial->cx, dial->cy, radius, ANGLE_START, dial->angle);
cairo_set_source_rgba_dim(cr, 1, 1, 1, alpha, dial->dim);
cairo_stroke(cr);
return;
}
// if the last breakpoint is at the upper limit, then the maximum
// value is displayed with the whole slider that colours
if (dial->level_breakpoint_angles[count - 1] == ANGLE_END &&
dial->angle == ANGLE_END) {
const double *colours = &dial->level_colours[(count - 1) * 3];
cairo_set_source_rgba_dim(
cr,
colours[0], colours[1], colours[2],
alpha,
dial->dim
);
cairo_arc(cr, dial->cx, dial->cy, radius, ANGLE_START, ANGLE_END);
cairo_stroke(cr);
return;
}
for (int i = 0; i < count; i++) {
const double *colours = &dial->level_colours[i * 3];
cairo_set_source_rgba_dim(
cr,
colours[0], colours[1], colours[2],
alpha,
dial->dim
);
double angle_start = dial->level_breakpoint_angles[i];
double angle_end =
i == count - 1
? ANGLE_END
: dial->level_breakpoint_angles[i + 1];
if (dial->angle < angle_end) {
cairo_arc(cr, dial->cx, dial->cy, radius, angle_start, dial->angle);
cairo_stroke(cr);
return;
}
cairo_arc(cr, dial->cx, dial->cy, radius, angle_start, angle_end);
cairo_stroke(cr);
}
}
static void dial_snapshot(GtkWidget *widget, GtkSnapshot *snapshot) {
@@ -940,6 +1011,18 @@ gboolean gtk_dial_get_can_control(GtkDial *dial) {
return dial->can_control;
}
void gtk_dial_set_level_meter_colours(
GtkDial *dial,
const int *breakpoints,
const double *colours,
int count
) {
dial->level_breakpoints = breakpoints;
dial->level_colours = colours;
dial->level_breakpoints_count = count;
dial->properties_updated = 1;
}
void gtk_dial_set_adjustment(GtkDial *dial, GtkAdjustment *adj) {
if (!(adj == NULL || GTK_IS_ADJUSTMENT(adj)))
return;
@@ -1217,6 +1300,8 @@ void gtk_dial_dispose(GObject *o) {
free(dial->taper_outputs);
dial->taper_outputs = NULL;
dial->taper_breakpoints_count = 0;
free(dial->level_breakpoint_angles);
dial->level_breakpoint_angles = NULL;
for (int focus = 0; focus <= 1; focus++)
for (int dim = 0; dim <= 1; dim++)

View File

@@ -87,6 +87,13 @@ void gtk_dial_set_taper_linear_breakpoints(
void gtk_dial_set_can_control(GtkDial *dial, gboolean can_control);
gboolean gtk_dial_get_can_control(GtkDial *dial);
void gtk_dial_set_level_meter_colours(
GtkDial *dial,
const int *breakpoints,
const double *colours,
int count
);
G_END_DECLS
#endif

View File

@@ -9,6 +9,20 @@
#include "widget-gain.h"
#include "window-levels.h"
static const int level_breakpoints_out[] = { -80, -18, -12, -6, -3, -1 };
// inputs glow all-red when limit is reached
static const int level_breakpoints_in[] = { -80, -18, -12, -6, -3, 0 };
static const double level_colours[] = {
0.00, 1.00, 0.00, // -80
0.75, 1.00, 0.00, // -18
1.00, 1.00, 0.00, // -12
1.00, 0.75, 0.00, // -6
1.00, 0.50, 0.00, // -3
1.00, 0.00, 0.00 // -1/0
};
static int update_levels_controls(void *user_data) {
struct alsa_card *card = user_data;
@@ -104,6 +118,14 @@ GtkWidget *create_levels_controls(struct alsa_card *card) {
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_level_meter_colours(
GTK_DIAL(meter),
(i == PC_DSP || i == PC_PCM)
? level_breakpoints_in
: level_breakpoints_out,
level_colours,
sizeof(level_breakpoints_out) / sizeof(int)
);
gtk_widget_set_sensitive(meter, FALSE);
// HW Output off_db is -55db; otherwise -45db