Add peak display to the level meters
This commit is contained in:
150
src/gtkdial.c
150
src/gtkdial.c
@@ -21,6 +21,8 @@
|
|||||||
#define DIAL_MIN_WIDTH 50
|
#define DIAL_MIN_WIDTH 50
|
||||||
#define DIAL_MAX_WIDTH 70
|
#define DIAL_MAX_WIDTH 70
|
||||||
|
|
||||||
|
#define HISTORY_COUNT 50
|
||||||
|
|
||||||
static int set_value(GtkDial *dial, double newval);
|
static int set_value(GtkDial *dial, double newval);
|
||||||
|
|
||||||
static void gtk_dial_set_property(
|
static void gtk_dial_set_property(
|
||||||
@@ -90,6 +92,7 @@ enum {
|
|||||||
PROP_OFF_DB,
|
PROP_OFF_DB,
|
||||||
PROP_TAPER,
|
PROP_TAPER,
|
||||||
PROP_CAN_CONTROL,
|
PROP_CAN_CONTROL,
|
||||||
|
PROP_PEAK_HOLD,
|
||||||
LAST_PROP
|
LAST_PROP
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,6 +120,7 @@ struct _GtkDial {
|
|||||||
double off_db;
|
double off_db;
|
||||||
int taper;
|
int taper;
|
||||||
gboolean can_control;
|
gboolean can_control;
|
||||||
|
int peak_hold;
|
||||||
|
|
||||||
int properties_updated;
|
int properties_updated;
|
||||||
|
|
||||||
@@ -155,6 +159,15 @@ struct _GtkDial {
|
|||||||
double angle;
|
double angle;
|
||||||
double slider_cx;
|
double slider_cx;
|
||||||
double slider_cy;
|
double slider_cy;
|
||||||
|
|
||||||
|
// same for the peak angle
|
||||||
|
double peak_angle;
|
||||||
|
|
||||||
|
// value history for displaying peak
|
||||||
|
double hist_values[HISTORY_COUNT];
|
||||||
|
long long hist_time[HISTORY_COUNT];
|
||||||
|
double current_peak;
|
||||||
|
int hist_head, hist_tail, hist_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE(GtkDial, gtk_dial, GTK_TYPE_WIDGET)
|
G_DEFINE_TYPE(GtkDial, gtk_dial, GTK_TYPE_WIDGET)
|
||||||
@@ -176,6 +189,17 @@ static void dial_measure(
|
|||||||
"move-slider", \
|
"move-slider", \
|
||||||
"(i)", scroll)
|
"(i)", scroll)
|
||||||
|
|
||||||
|
long long current_time = 0;
|
||||||
|
|
||||||
|
void gtk_dial_peak_tick(void) {
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_BOOTTIME, &ts) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
current_time = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
// BEGIN SECTION HELPERS
|
// BEGIN SECTION HELPERS
|
||||||
|
|
||||||
#define TOTAL_ROTATION_DEGREES 290
|
#define TOTAL_ROTATION_DEGREES 290
|
||||||
@@ -404,6 +428,12 @@ static void update_dial_values(GtkDial *dial) {
|
|||||||
dial->angle = calc_val(dial->valp, ANGLE_START, ANGLE_END);
|
dial->angle = calc_val(dial->valp, ANGLE_START, ANGLE_END);
|
||||||
dial->slider_cx = cos(dial->angle) * dial->slider_radius + dial->cx;
|
dial->slider_cx = cos(dial->angle) * dial->slider_radius + dial->cx;
|
||||||
dial->slider_cy = sin(dial->angle) * dial->slider_radius + dial->cy;
|
dial->slider_cy = sin(dial->angle) * dial->slider_radius + dial->cy;
|
||||||
|
|
||||||
|
if (!dial->peak_hold)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double peak_valp = calc_taper(dial, dial->current_peak);
|
||||||
|
dial->peak_angle = calc_val(peak_valp, ANGLE_START, ANGLE_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
static double pdist2(double x1, double y1, double x2, double y2) {
|
static double pdist2(double x1, double y1, double x2, double y2) {
|
||||||
@@ -530,6 +560,20 @@ static void gtk_dial_class_init(GtkDialClass *klass) {
|
|||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GtkDial:peak-hold: (attributes org.gtk.Method.get=gtk_dial_get_peak_hold org.gtk.Method.set=gtk_dial_set_peak_hold)
|
||||||
|
*
|
||||||
|
* The number of milliseconds to hold the peak value.
|
||||||
|
*/
|
||||||
|
properties[PROP_PEAK_HOLD] = g_param_spec_int(
|
||||||
|
"peak-hold",
|
||||||
|
"PeakHold",
|
||||||
|
"The number of milliseconds to hold the peak value",
|
||||||
|
0, 1000,
|
||||||
|
0,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT
|
||||||
|
);
|
||||||
|
|
||||||
g_object_class_install_properties(g_class, LAST_PROP, properties);
|
g_object_class_install_properties(g_class, LAST_PROP, properties);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -648,6 +692,11 @@ static void gtk_dial_init(GtkDial *dial) {
|
|||||||
g_signal_connect(
|
g_signal_connect(
|
||||||
dial, "notify::sensitive", G_CALLBACK(gtk_dial_notify_sensitive_cb), dial
|
dial, "notify::sensitive", G_CALLBACK(gtk_dial_notify_sensitive_cb), dial
|
||||||
);
|
);
|
||||||
|
|
||||||
|
dial->current_peak = -INFINITY;
|
||||||
|
dial->hist_head = 0;
|
||||||
|
dial->hist_tail = 0;
|
||||||
|
dial->hist_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dial_measure(
|
static void dial_measure(
|
||||||
@@ -686,6 +735,42 @@ static void cairo_set_source_rgba_dim(
|
|||||||
cairo_set_source_rgba(cr, r, g, b, a);
|
cairo_set_source_rgba(cr, r, g, b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void draw_peak(GtkDial *dial, cairo_t *cr, double radius) {
|
||||||
|
|
||||||
|
double angle_start = dial->peak_angle - M_PI / 180;
|
||||||
|
if (angle_start < ANGLE_START)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// determine the colour of the peak
|
||||||
|
int count = dial->level_breakpoints_count;
|
||||||
|
|
||||||
|
// if there are no colours, don't draw the peak
|
||||||
|
if (!count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count - 1; i++)
|
||||||
|
if (dial->current_peak < dial->level_breakpoints[i + 1])
|
||||||
|
break;
|
||||||
|
|
||||||
|
const double *colours = &dial->level_colours[i * 3];
|
||||||
|
|
||||||
|
cairo_set_source_rgba_dim(
|
||||||
|
cr, colours[0], colours[1], colours[2], 0.5, dial->dim
|
||||||
|
);
|
||||||
|
cairo_set_line_width(cr, 2);
|
||||||
|
cairo_arc(cr, dial->cx, dial->cy, radius, ANGLE_START, dial->peak_angle);
|
||||||
|
cairo_stroke(cr);
|
||||||
|
|
||||||
|
cairo_set_source_rgba_dim(
|
||||||
|
cr, colours[0], colours[1], colours[2], 1, dial->dim
|
||||||
|
);
|
||||||
|
cairo_set_line_width(cr, 4);
|
||||||
|
cairo_arc(cr, dial->cx, dial->cy, radius, angle_start, dial->peak_angle);
|
||||||
|
cairo_stroke(cr);
|
||||||
|
}
|
||||||
|
|
||||||
static void draw_slider(
|
static void draw_slider(
|
||||||
GtkDial *dial,
|
GtkDial *dial,
|
||||||
cairo_t *cr,
|
cairo_t *cr,
|
||||||
@@ -781,6 +866,10 @@ static void dial_snapshot(GtkWidget *widget, GtkSnapshot *snapshot) {
|
|||||||
draw_slider(dial, cr, dial->slider_radius, 6, 0.3);
|
draw_slider(dial, cr, dial->slider_radius, 6, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// peak hold
|
||||||
|
if (dial->peak_hold)
|
||||||
|
draw_peak(dial, cr, dial->slider_radius);
|
||||||
|
|
||||||
// draw line to zero db
|
// draw line to zero db
|
||||||
double zero_db = gtk_dial_get_zero_db(dial);
|
double zero_db = gtk_dial_get_zero_db(dial);
|
||||||
if (zero_db != -G_MAXDOUBLE) {
|
if (zero_db != -G_MAXDOUBLE) {
|
||||||
@@ -903,6 +992,9 @@ static void gtk_dial_set_property(
|
|||||||
case PROP_CAN_CONTROL:
|
case PROP_CAN_CONTROL:
|
||||||
gtk_dial_set_can_control(dial, g_value_get_boolean(value));
|
gtk_dial_set_can_control(dial, g_value_get_boolean(value));
|
||||||
break;
|
break;
|
||||||
|
case PROP_PEAK_HOLD:
|
||||||
|
gtk_dial_set_peak_hold(dial, g_value_get_int(value));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -936,6 +1028,9 @@ static void gtk_dial_get_property(
|
|||||||
case PROP_CAN_CONTROL:
|
case PROP_CAN_CONTROL:
|
||||||
g_value_set_boolean(value, dial->can_control);
|
g_value_set_boolean(value, dial->can_control);
|
||||||
break;
|
break;
|
||||||
|
case PROP_PEAK_HOLD:
|
||||||
|
g_value_set_int(value, dial->peak_hold);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -1044,6 +1139,14 @@ void gtk_dial_set_level_meter_colours(
|
|||||||
dial->properties_updated = 1;
|
dial->properties_updated = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gtk_dial_set_peak_hold(GtkDial *dial, int peak_hold) {
|
||||||
|
dial->peak_hold = peak_hold;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gtk_dial_get_peak_hold(GtkDial *dial) {
|
||||||
|
return dial->peak_hold;
|
||||||
|
}
|
||||||
|
|
||||||
void gtk_dial_set_adjustment(GtkDial *dial, GtkAdjustment *adj) {
|
void gtk_dial_set_adjustment(GtkDial *dial, GtkAdjustment *adj) {
|
||||||
if (!(adj == NULL || GTK_IS_ADJUSTMENT(adj)))
|
if (!(adj == NULL || GTK_IS_ADJUSTMENT(adj)))
|
||||||
return;
|
return;
|
||||||
@@ -1059,6 +1162,46 @@ GtkAdjustment *gtk_dial_get_adjustment(GtkDial *dial) {
|
|||||||
return dial->adj;
|
return dial->adj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gtk_dial_add_hist_value(GtkDial *dial, double value) {
|
||||||
|
|
||||||
|
int need_peak_update = 0;
|
||||||
|
|
||||||
|
// remove the oldest value(s) if they are too old or if the history
|
||||||
|
// is full
|
||||||
|
while (dial->hist_count > 0 &&
|
||||||
|
(dial->hist_time[dial->hist_head] < current_time - dial->peak_hold ||
|
||||||
|
dial->hist_count == HISTORY_COUNT)) {
|
||||||
|
|
||||||
|
// check if the value removed is the current peak
|
||||||
|
if (dial->hist_values[dial->hist_head] >= dial->current_peak)
|
||||||
|
need_peak_update = 1;
|
||||||
|
|
||||||
|
// move the head forward
|
||||||
|
dial->hist_head = (dial->hist_head + 1) % HISTORY_COUNT;
|
||||||
|
dial->hist_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recalculate the peak if needed
|
||||||
|
if (need_peak_update) {
|
||||||
|
dial->current_peak = -INFINITY;
|
||||||
|
for (int i = dial->hist_head;
|
||||||
|
i != dial->hist_tail;
|
||||||
|
i = (i + 1) % HISTORY_COUNT)
|
||||||
|
if (dial->hist_values[i] > dial->current_peak)
|
||||||
|
dial->current_peak = dial->hist_values[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the new value
|
||||||
|
dial->hist_values[dial->hist_tail] = value;
|
||||||
|
dial->hist_time[dial->hist_tail] = current_time;
|
||||||
|
dial->hist_tail = (dial->hist_tail + 1) % HISTORY_COUNT;
|
||||||
|
dial->hist_count++;
|
||||||
|
|
||||||
|
// update the peak if needed
|
||||||
|
if (value > dial->current_peak)
|
||||||
|
dial->current_peak = value;
|
||||||
|
}
|
||||||
|
|
||||||
static int set_value(GtkDial *dial, double newval) {
|
static int set_value(GtkDial *dial, double newval) {
|
||||||
if (dial->round_digits >= 0) {
|
if (dial->round_digits >= 0) {
|
||||||
double power;
|
double power;
|
||||||
@@ -1079,7 +1222,10 @@ static int set_value(GtkDial *dial, double newval) {
|
|||||||
|
|
||||||
double oldval = gtk_adjustment_get_value(dial->adj);
|
double oldval = gtk_adjustment_get_value(dial->adj);
|
||||||
|
|
||||||
if (oldval == newval)
|
double old_peak = dial->current_peak;
|
||||||
|
gtk_dial_add_hist_value(dial, newval);
|
||||||
|
|
||||||
|
if (oldval == newval && old_peak == dial->current_peak)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
gtk_adjustment_set_value(dial->adj, newval);
|
gtk_adjustment_set_value(dial->adj, newval);
|
||||||
@@ -1088,7 +1234,7 @@ static int set_value(GtkDial *dial, double newval) {
|
|||||||
double old_valp = dial->valp;
|
double old_valp = dial->valp;
|
||||||
update_dial_values(dial);
|
update_dial_values(dial);
|
||||||
|
|
||||||
return old_valp != dial->valp;
|
return old_valp != dial->valp || old_peak != dial->current_peak;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void step_back(GtkDial *dial) {
|
static void step_back(GtkDial *dial) {
|
||||||
|
|||||||
@@ -94,6 +94,10 @@ void gtk_dial_set_level_meter_colours(
|
|||||||
int count
|
int count
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void gtk_dial_set_peak_hold(GtkDial *dial, int peak_hold);
|
||||||
|
int gtk_dial_get_peak_hold(GtkDial *dial);
|
||||||
|
void gtk_dial_peak_tick(void);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ static int update_levels_controls(void *user_data) {
|
|||||||
|
|
||||||
int meter_num = 0;
|
int meter_num = 0;
|
||||||
|
|
||||||
|
gtk_dial_peak_tick();
|
||||||
|
|
||||||
// go through the port categories
|
// go through the port categories
|
||||||
for (int i = 0; i < PC_COUNT; i++) {
|
for (int i = 0; i < PC_COUNT; i++) {
|
||||||
|
|
||||||
@@ -125,6 +127,7 @@ GtkWidget *create_levels_controls(struct alsa_card *card) {
|
|||||||
GtkWidget *meter = gtk_dial_new_with_range(-80, 0, 0, 0);
|
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_taper(GTK_DIAL(meter), GTK_DIAL_TAPER_LINEAR);
|
||||||
gtk_dial_set_can_control(GTK_DIAL(meter), FALSE);
|
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_set_level_meter_colours(
|
||||||
GTK_DIAL(meter),
|
GTK_DIAL(meter),
|
||||||
(i == PC_DSP || i == PC_PCM)
|
(i == PC_DSP || i == PC_PCM)
|
||||||
|
|||||||
Reference in New Issue
Block a user