1 /*
2 * lingot, a musical instrument tuner.
3 *
4 * Copyright (C) 2004-2018 Iban Cereijo.
5 * Copyright (C) 2004-2008 Jairo Chapela.
6
7 *
8 * This file is part of lingot.
9 *
10 * lingot is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * lingot is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with lingot; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #include <stdio.h>
26 #include <math.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include <string.h>
30 #include <errno.h>
31
32 #include "lingot-defs.h"
33
34 #include "lingot-config.h"
35 #include "lingot-gui-mainframe.h"
36 #include "lingot-gui-config-dialog.h"
37 #include "lingot-gauge.h"
38 #include "lingot-i18n.h"
39 #include "lingot-io-config.h"
40 #include "lingot-msg.h"
41
42 static void lingot_gui_mainframe_draw_gauge_background(cairo_t *cr, const LingotMainFrame* frame);
43 static void lingot_gui_mainframe_draw_spectrum_background(cairo_t *cr, const LingotMainFrame* frame);
44 void lingot_gui_mainframe_draw_gauge(cairo_t *cr, const LingotMainFrame*);
45 void lingot_gui_mainframe_draw_spectrum(cairo_t *cr, const LingotMainFrame*);
46 void lingot_gui_mainframe_draw_labels(const LingotMainFrame*);
47
48 // sizes
49
50 static int gauge_size_x = 0;
51 static int gauge_size_y = 0;
52
53 static int spectrum_size_x = 0;
54 static int spectrum_size_y = 0;
55
56 static FLT spectrum_bottom_margin;
57 static FLT spectrum_top_margin;
58 static FLT spectrum_left_margin;
59 static FLT spectrum_right_margin;
60
61 static FLT spectrum_inner_x;
62 static FLT spectrum_inner_y;
63
64 static const FLT spectrum_min_db = 0; // TODO
65 static const FLT spectrum_max_db = 52;
66 static FLT spectrum_db_density = 0;
67
68 static int labelsbox_size_x = 0;
69 static int labelsbox_size_y = 0;
70
71 static gchar* filechooser_config_last_folder = NULL;
72
73 static const gdouble aspect_ratio_spectrum_visible = 1.14;
74 static const gdouble aspect_ratio_spectrum_invisible = 2.07;
75
76 // TODO: keep here?
77 static int closest_note_index = 0;
78 static FLT frequency = 0.0;
79
lingot_gui_mainframe_callback_redraw_gauge(GtkWidget * w,cairo_t * cr,const LingotMainFrame * data)80 void lingot_gui_mainframe_callback_redraw_gauge(GtkWidget *w, cairo_t *cr, const LingotMainFrame* data) {
81 (void)w; // Unused parameter.
82 lingot_gui_mainframe_draw_gauge(cr, data);
83 }
84
lingot_gui_mainframe_callback_redraw_spectrum(GtkWidget * w,cairo_t * cr,const LingotMainFrame * frame)85 void lingot_gui_mainframe_callback_redraw_spectrum(GtkWidget* w, cairo_t *cr, const LingotMainFrame* frame) {
86 (void)w; // Unused parameter.
87 lingot_gui_mainframe_draw_spectrum(cr, frame);
88 }
89
lingot_gui_mainframe_callback_destroy(GtkWidget * w,LingotMainFrame * frame)90 void lingot_gui_mainframe_callback_destroy(GtkWidget* w, LingotMainFrame* frame) {
91 (void)w; // Unused parameter.
92 g_source_remove(frame->visualization_timer_uid);
93 g_source_remove(frame->freq_computation_timer_uid);
94 g_source_remove(frame->gauge_computation_uid);
95 gtk_main_quit();
96 }
97
lingot_gui_mainframe_callback_about(GtkWidget * w,LingotMainFrame * frame)98 void lingot_gui_mainframe_callback_about(GtkWidget* w, LingotMainFrame* frame) {
99 (void)w; // Unused parameter.
100 (void)frame; // Unused parameter.
101 static const gchar* authors[] = {
102 "Iban Cereijo <ibancg@gmail.com>",
103 "Jairo Chapela <jairochapela@gmail.com>",
104 NULL };
105
106 char buff[512];
107 snprintf(buff, sizeof(buff), "Matthew Blissett (%s)", _("Logo design"));
108 const gchar* artists[] = { buff, NULL };
109
110 GError *error = NULL;
111 GtkIconTheme *icon_theme = NULL;
112 GdkPixbuf *pixbuf = NULL;
113
114 // we use the property "logo" instead of "logo-icon-name", so we can specify
115 // here at what size we want to scale the icon in this dialog
116 icon_theme = gtk_icon_theme_get_default ();
117 pixbuf = gtk_icon_theme_load_icon (icon_theme,
118 "org.nongnu.lingot", // icon name
119 80, // icon size
120 0, // flags
121 &error);
122
123 if (error) {
124 g_warning("Couldn’t load icon: %s", error->message);
125 g_error_free(error);
126 }
127
128 gtk_show_about_dialog(NULL,
129 "name", "Lingot",
130 "version", VERSION,
131 "copyright", "\xC2\xA9 2004-2018 Iban Cereijo\n\xC2\xA9 2004-2018 Jairo Chapela",
132 "comments", _("Accurate and easy to use musical instrument tuner"),
133 "authors", authors,
134 "artists", artists,
135 "website-label", "https://www.nongnu.org/lingot/",
136 "website", "https://www.nongnu.org/lingot/",
137 "license-type", GTK_LICENSE_GPL_2_0,
138 "translator-credits", _("translator-credits"),
139 //"logo-icon-name", "org.nongnu.lingot",
140 "logo", pixbuf,
141 NULL);
142
143 if (pixbuf) {
144 g_object_unref(pixbuf);
145 }
146 }
147
lingot_gui_mainframe_callback_view_spectrum(GtkWidget * w,LingotMainFrame * frame)148 void lingot_gui_mainframe_callback_view_spectrum(GtkWidget* w, LingotMainFrame* frame) {
149 (void)w; // Unused parameter.
150 gboolean visible = gtk_check_menu_item_get_active(
151 GTK_CHECK_MENU_ITEM(frame->view_spectrum_item));
152
153 GtkAllocation alloc;
154 gtk_widget_get_allocation(frame->win, &alloc);
155 GdkGeometry hints;
156 gdouble aspect_ratio =
157 visible ?
158 aspect_ratio_spectrum_visible :
159 aspect_ratio_spectrum_invisible;
160 hints.min_aspect = aspect_ratio;
161 hints.max_aspect = aspect_ratio;
162 gtk_window_set_geometry_hints(GTK_WINDOW(frame->win), frame->win, &hints,
163 GDK_HINT_ASPECT);
164
165 gtk_widget_set_visible(frame->spectrum_frame, visible);
166 }
167
lingot_gui_mainframe_callback_config_dialog(GtkWidget * w,LingotMainFrame * frame)168 void lingot_gui_mainframe_callback_config_dialog(GtkWidget* w,
169 LingotMainFrame* frame) {
170 (void)w; // Unused parameter.
171 lingot_gui_config_dialog_show(frame, NULL);
172 }
173
174 /* timeout for gauge and labels visualization */
lingot_gui_mainframe_callback_tout_visualization(gpointer data)175 gboolean lingot_gui_mainframe_callback_tout_visualization(gpointer data) {
176 unsigned int period;
177
178 LingotMainFrame* frame = (LingotMainFrame*) data;
179
180 period = 1000 / frame->conf.visualization_rate;
181 frame->visualization_timer_uid = g_timeout_add(period,
182 lingot_gui_mainframe_callback_tout_visualization, frame);
183
184 gtk_widget_queue_draw(frame->gauge_area);
185
186 return 0;
187 }
188
189 /* timeout for spectrum computation and display */
lingot_gui_mainframe_callback_tout_spectrum_computation_display(gpointer data)190 gboolean lingot_gui_mainframe_callback_tout_spectrum_computation_display(
191 gpointer data) {
192 unsigned int period;
193
194 LingotMainFrame* frame = (LingotMainFrame*) data;
195
196 period = 1000 / frame->conf.calculation_rate;
197 frame->freq_computation_timer_uid = g_timeout_add(period,
198 lingot_gui_mainframe_callback_tout_spectrum_computation_display,
199 frame);
200
201 gtk_widget_queue_draw(frame->spectrum_area);
202 lingot_gui_mainframe_draw_labels(frame);
203
204 return 0;
205 }
206
207 /* timeout for a new gauge position computation */
lingot_gui_mainframe_callback_gauge_computation(gpointer data)208 gboolean lingot_gui_mainframe_callback_gauge_computation(gpointer data) {
209 unsigned int period;
210 LingotMainFrame* frame = (LingotMainFrame*) data;
211
212 period = 1000 / GAUGE_RATE;
213 frame->gauge_computation_uid = g_timeout_add(period,
214 lingot_gui_mainframe_callback_gauge_computation, frame);
215
216 // ignore continuous component
217 if (!frame->core.running || isnan(frame->core.freq)
218 || (frame->core.freq <= frame->conf.internal_min_frequency)) {
219 frequency = 0.0;
220 lingot_gauge_compute(&frame->gauge, frame->conf.gauge_rest_value);
221 } else {
222 FLT error_cents; // do not use, unfiltered
223 frequency = lingot_filter_filter_sample(&frame->freq_filter,
224 frame->core.freq);
225 closest_note_index = lingot_config_scale_get_closest_note_index(
226 &frame->conf.scale, frame->core.freq,
227 frame->conf.root_frequency_error, &error_cents);
228 if (!isnan(error_cents)) {
229 lingot_gauge_compute(&frame->gauge, error_cents);
230 }
231 }
232
233 return 0;
234 }
235
236 /* timeout for dispatching the error queue */
lingot_gui_mainframe_callback_error_dispatcher(gpointer data)237 gboolean lingot_gui_mainframe_callback_error_dispatcher(gpointer data) {
238 unsigned int period;
239 GtkWidget* message_dialog;
240 LingotMainFrame* frame = (LingotMainFrame*) data;
241
242 char* error_message = NULL;
243 message_type_t message_type;
244 int error_code;
245 int more_messages;
246
247 do {
248 more_messages = lingot_msg_get(&error_message, &message_type, &error_code);
249
250 if (more_messages) {
251 GtkWindow* parent =
252 GTK_WINDOW(
253 (frame->config_dialog != NULL) ? frame->config_dialog->win : frame->win);
254 GtkButtonsType buttonsType;
255
256 char message[2000];
257 char* message_pointer = message;
258
259 message_pointer += snprintf(message_pointer,
260 (message - message_pointer) + sizeof(message), "%s",
261 error_message);
262
263 if (error_code == EBUSY) {
264 message_pointer +=
265 snprintf(message_pointer,
266 (message - message_pointer) + sizeof(message),
267 "\n\n%s",
268 _(
269 "Please check that there are not other processes locking the requested device. Also, consider that some audio servers can sometimes hold the resources for a few seconds since the last time they were used. In such a case, you can try again."));
270 }
271
272 if ((message_type == ERROR) && !frame->core.running) {
273 buttonsType = GTK_BUTTONS_OK;
274 message_pointer +=
275 snprintf(message_pointer,
276 (message - message_pointer) + sizeof(message),
277 "\n\n%s",
278 _(
279 "The core is not running, you must check your configuration."));
280 } else {
281 buttonsType = GTK_BUTTONS_OK;
282 }
283
284 message_dialog = gtk_message_dialog_new(parent,
285 GTK_DIALOG_DESTROY_WITH_PARENT,
286 (message_type == ERROR) ? GTK_MESSAGE_ERROR :
287 ((message_type == WARNING) ? GTK_MESSAGE_WARNING : GTK_MESSAGE_INFO),
288 buttonsType, "%s", message);
289
290 gtk_window_set_title(GTK_WINDOW(message_dialog),
291 (message_type == ERROR) ? _("Error") :
292 ((message_type == WARNING) ? _("Warning") : _("Info")));
293 gtk_window_set_icon(GTK_WINDOW(message_dialog),
294 gtk_window_get_icon(GTK_WINDOW(frame->win)));
295 gtk_dialog_run(GTK_DIALOG(message_dialog));
296 gtk_widget_destroy(message_dialog);
297 free(error_message);
298
299 // if ((message_type == ERROR) && !frame->core.running) {
300 // lingot_gui_mainframe_callback_config_dialog(NULL, frame);
301 // }
302
303 }
304 } while (more_messages);
305
306 period = 1000 / ERROR_DISPATCH_RATE;
307 frame->error_dispatcher_uid = g_timeout_add(period,
308 lingot_gui_mainframe_callback_error_dispatcher, frame);
309
310 return 0;
311 }
312
lingot_gui_mainframe_callback_open_config(gpointer data,LingotMainFrame * frame)313 void lingot_gui_mainframe_callback_open_config(gpointer data,
314 LingotMainFrame* frame) {
315 (void)data; // Unused parameter.
316 GtkWidget * dialog = gtk_file_chooser_dialog_new(
317 _("Open Configuration File"), GTK_WINDOW(frame->win),
318 GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL,
319 "_Open", GTK_RESPONSE_ACCEPT, NULL);
320 GtkFileFilter *filefilter;
321 char config_used = 0;
322 LingotConfig config;
323 filefilter = gtk_file_filter_new();
324
325 gtk_file_filter_set_name(filefilter,
326 (const gchar *) _("Lingot configuration files"));
327 gtk_file_filter_add_pattern(filefilter, "*.conf");
328 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filefilter);
329 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
330
331 if (filechooser_config_last_folder != NULL) {
332 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
333 filechooser_config_last_folder);
334 }
335
336 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
337 char *filename;
338 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
339 free(filechooser_config_last_folder);
340 filechooser_config_last_folder = strdup(
341 gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog)));
342 lingot_config_new(&config);
343 lingot_io_config_load(&config, filename);
344 config_used = 1;
345 g_free(filename);
346 }
347 gtk_widget_destroy(dialog);
348 //g_free(filefilter);
349
350 if (config_used) {
351 lingot_gui_config_dialog_show(frame, &config);
352 }
353 }
354
lingot_gui_mainframe_callback_save_config(gpointer data,LingotMainFrame * frame)355 void lingot_gui_mainframe_callback_save_config(gpointer data, LingotMainFrame* frame) {
356 (void)data; // Unused parameter.
357 GtkWidget *dialog = gtk_file_chooser_dialog_new(
358 _("Save Configuration File"), GTK_WINDOW(frame->win),
359 GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL,
360 "_Save", GTK_RESPONSE_ACCEPT, NULL);
361 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
362 TRUE);
363
364 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), _("untitled.conf"));
365 GtkFileFilter* filefilter = gtk_file_filter_new();
366
367 gtk_file_filter_set_name(filefilter,
368 (const gchar *) _("Lingot configuration files"));
369 gtk_file_filter_add_pattern(filefilter, "*.conf");
370 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filefilter);
371 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
372
373 if (filechooser_config_last_folder != NULL) {
374 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
375 filechooser_config_last_folder);
376 }
377
378 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
379 char *filename;
380 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
381 if (filechooser_config_last_folder != NULL)
382 free(filechooser_config_last_folder);
383 filechooser_config_last_folder = strdup(
384 gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog)));
385 lingot_io_config_save(&frame->conf, filename);
386 g_free(filename);
387 }
388 gtk_widget_destroy(dialog);
389 }
390
lingot_gui_mainframe_color(GdkColor * color,int red,int green,int blue)391 void lingot_gui_mainframe_color(GdkColor* color, int red, int green, int blue) {
392 color->red = red;
393 color->green = green;
394 color->blue = blue;
395 }
396
lingot_gui_mainframe_callback_window_resize(GtkWidget * widget,GtkAllocation * allocation,void * data)397 void lingot_gui_mainframe_callback_window_resize(GtkWidget *widget,
398 GtkAllocation *allocation, void *data) {
399 (void)widget; // Unused parameter.
400 (void)allocation; // Unused parameter.
401
402 const LingotMainFrame* frame = (LingotMainFrame*) data;
403
404 GtkAllocation req;
405 gtk_widget_get_allocation(frame->gauge_area, &req);
406
407 if ((req.width != gauge_size_x) || (req.height != gauge_size_y)) {
408 gauge_size_x = req.width;
409 gauge_size_y = req.height;
410 }
411
412 gtk_widget_get_allocation(frame->spectrum_area, &req);
413
414 if ((req.width != spectrum_size_x) || (req.height != spectrum_size_y)) {
415 spectrum_size_x = req.width;
416 spectrum_size_y = req.height;
417 }
418
419 gtk_widget_get_allocation(frame->labelsbox, &req);
420
421 labelsbox_size_x = req.width;
422 labelsbox_size_y = req.height;
423 }
424
lingot_gui_mainframe_create(int argc,char * argv[])425 void lingot_gui_mainframe_create(int argc, char *argv[]) {
426
427 LingotMainFrame* frame;
428
429 if (filechooser_config_last_folder == NULL) {
430 char buff[1000];
431 snprintf(buff, sizeof(buff), "%s/%s", getenv("HOME"), CONFIG_DIR_NAME);
432 filechooser_config_last_folder = strdup(buff);
433 }
434
435 frame = malloc(sizeof(LingotMainFrame));
436
437 frame->config_dialog = NULL;
438
439 LingotConfig* const conf = &frame->conf;
440 lingot_config_new(conf);
441 lingot_io_config_load(conf, CONFIG_FILE_NAME);
442
443 lingot_gauge_new(&frame->gauge, conf->gauge_rest_value); // gauge in rest situation
444
445 // ----- FREQUENCY FILTER CONFIGURATION ------
446
447 // low pass IIR filter.
448 FLT freq_filter_a[] = { 1.0, -0.5 };
449 FLT freq_filter_b[] = { 0.5 };
450
451 lingot_filter_new(&frame->freq_filter, 1, 0, freq_filter_a, freq_filter_b);
452
453 // ---------------------------------------------------
454
455 gtk_init(&argc, &argv);
456 // gtk_set_locale();
457
458 GtkBuilder* builder = gtk_builder_new();
459
460 gtk_builder_add_from_resource(builder, "/org/nongnu/lingot/lingot-gui-mainframe.glade", NULL);
461
462 frame->win = GTK_WIDGET(gtk_builder_get_object(builder, "window1"));
463
464 gtk_window_set_default_icon_name("org.nongnu.lingot");
465 gtk_window_set_icon_name(GTK_WINDOW(frame->win), "org.nongnu.lingot");
466
467 frame->gauge_area = GTK_WIDGET(
468 gtk_builder_get_object(builder, "gauge_area"));
469 frame->spectrum_area = GTK_WIDGET(
470 gtk_builder_get_object(builder, "spectrum_area"));
471
472 frame->freq_label = GTK_WIDGET(
473 gtk_builder_get_object(builder, "freq_label"));
474 frame->tone_label = GTK_WIDGET(
475 gtk_builder_get_object(builder, "tone_label"));
476 frame->error_label = GTK_WIDGET(
477 gtk_builder_get_object(builder, "error_label"));
478
479 frame->spectrum_frame = GTK_WIDGET(
480 gtk_builder_get_object(builder, "spectrum_frame"));
481 frame->view_spectrum_item = GTK_WIDGET(
482 gtk_builder_get_object(builder, "spectrum_item"));
483 frame->labelsbox = GTK_WIDGET(gtk_builder_get_object(builder, "labelsbox"));
484
485 gtk_check_menu_item_set_active(
486 GTK_CHECK_MENU_ITEM(frame->view_spectrum_item), TRUE);
487
488 // show all
489 gtk_widget_show_all(frame->win);
490
491 GtkAllocation alloc;
492 gtk_widget_get_allocation(frame->win, &alloc);
493 GdkGeometry hints;
494 gdouble aspect_ratio = aspect_ratio_spectrum_visible;
495 hints.min_aspect = aspect_ratio;
496 hints.max_aspect = aspect_ratio;
497 gtk_window_set_geometry_hints(GTK_WINDOW(frame->win), frame->win, &hints,
498 GDK_HINT_ASPECT);
499
500 // GTK signals
501 g_signal_connect(gtk_builder_get_object(builder, "preferences_item"),
502 "activate", G_CALLBACK(lingot_gui_mainframe_callback_config_dialog),
503 frame);
504 g_signal_connect(gtk_builder_get_object(builder, "quit_item"), "activate",
505 G_CALLBACK(lingot_gui_mainframe_callback_destroy), frame);
506 g_signal_connect(gtk_builder_get_object(builder, "about_item"), "activate",
507 G_CALLBACK(lingot_gui_mainframe_callback_about), frame);
508 g_signal_connect(gtk_builder_get_object(builder, "spectrum_item"),
509 "activate", G_CALLBACK(lingot_gui_mainframe_callback_view_spectrum),
510 frame);
511 g_signal_connect(gtk_builder_get_object(builder, "open_config_item"),
512 "activate", G_CALLBACK(lingot_gui_mainframe_callback_open_config),
513 frame);
514 g_signal_connect(gtk_builder_get_object(builder, "save_config_item"),
515 "activate", G_CALLBACK(lingot_gui_mainframe_callback_save_config),
516 frame);
517
518 g_signal_connect(frame->gauge_area, "draw",
519 G_CALLBACK(lingot_gui_mainframe_callback_redraw_gauge), frame);
520 g_signal_connect(frame->spectrum_area, "draw",
521 G_CALLBACK(lingot_gui_mainframe_callback_redraw_spectrum), frame);
522 g_signal_connect(frame->win, "destroy",
523 G_CALLBACK(lingot_gui_mainframe_callback_destroy), frame);
524
525 // TODO: remove
526 g_signal_connect(frame->win, "size-allocate",
527 G_CALLBACK(lingot_gui_mainframe_callback_window_resize), frame);
528
529 GtkAccelGroup* accel_group = gtk_accel_group_new();
530 gtk_widget_add_accelerator(
531 GTK_WIDGET(gtk_builder_get_object(builder, "preferences_item")),
532 "activate", accel_group, 'p', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
533 gtk_window_add_accel_group(GTK_WINDOW(frame->win), accel_group);
534
535 unsigned int period;
536 period = 1000 / conf->visualization_rate;
537 frame->visualization_timer_uid = g_timeout_add(period,
538 lingot_gui_mainframe_callback_tout_visualization, frame);
539
540 period = 1000 / conf->calculation_rate;
541 frame->freq_computation_timer_uid = g_timeout_add(period,
542 lingot_gui_mainframe_callback_tout_spectrum_computation_display,
543 frame);
544
545 period = 1000 / GAUGE_RATE;
546 frame->gauge_computation_uid = g_timeout_add(period,
547 lingot_gui_mainframe_callback_gauge_computation, frame);
548
549 period = 1000 / ERROR_DISPATCH_RATE;
550 frame->error_dispatcher_uid = g_timeout_add(period,
551 lingot_gui_mainframe_callback_error_dispatcher, frame);
552
553 lingot_core_new(&frame->core, conf);
554 lingot_core_start(&frame->core);
555
556 g_object_unref(builder);
557
558 gtk_main();
559 }
560
lingot_gui_mainframe_destroy(LingotMainFrame * frame)561 void lingot_gui_mainframe_destroy(LingotMainFrame* frame) {
562
563 lingot_core_stop(&frame->core);
564 lingot_core_destroy(&frame->core);
565
566 lingot_gauge_destroy(&frame->gauge);
567 lingot_filter_destroy(&frame->freq_filter);
568 lingot_config_destroy(&frame->conf);
569 if (frame->config_dialog) {
570 lingot_gui_config_dialog_destroy(frame->config_dialog);
571 }
572
573 free(frame);
574 }
575
576 // ---------------------------------------------------------------------------
577
lingot_gui_mainframe_cairo_set_source_argb(cairo_t * cr,unsigned int color)578 static void lingot_gui_mainframe_cairo_set_source_argb(cairo_t *cr,
579 unsigned int color) {
580 cairo_set_source_rgba(cr,
581 0.00392156862745098 * ((color >> 16) & 0xff),
582 0.00392156862745098 * ((color >> 8) & 0xff),
583 0.00392156862745098 * (color & 0xff),
584 1.0 - 0.00392156862745098 * ((color >> 24) & 0xff));
585 }
586
587 typedef struct {
588 FLT x;
589 FLT y;
590 } point_t;
591
lingot_gui_mainframe_draw_gauge_tic(cairo_t * cr,const point_t * gaugeCenter,double radius1,double radius2,double angle)592 static void lingot_gui_mainframe_draw_gauge_tic(cairo_t *cr,
593 const point_t* gaugeCenter, double radius1, double radius2,
594 double angle) {
595 cairo_move_to(cr, gaugeCenter->x + radius1 * sin(angle),
596 gaugeCenter->y - radius1 * cos(angle));
597 cairo_rel_line_to(cr, (radius2 - radius1) * sin(angle),
598 (radius1 - radius2) * cos(angle));
599 cairo_stroke(cr);
600 }
601
lingot_gui_mainframe_draw_gauge_background(cairo_t * cr,const LingotMainFrame * frame)602 static void lingot_gui_mainframe_draw_gauge_background(cairo_t *cr,
603 const LingotMainFrame* frame) {
604
605 // normalized dimensions
606 static const FLT gauge_gaugeCenterY = 0.94;
607 static const FLT gauge_centsBarStroke = 0.025;
608 static const FLT gauge_centsBarRadius = 0.75;
609 static const FLT gauge_centsBarMajorTicRadius = 0.04;
610 static const FLT gauge_centsBarMinorTicRadius = 0.03;
611 static const FLT gauge_centsBarMajorTicStroke = 0.03;
612 static const FLT gauge_centsBarMinorTicStroke = 0.01;
613 static const FLT gauge_centsTextSize = 0.09;
614 static const FLT gauge_frequencyBarStroke = 0.025;
615 static const FLT gauge_frequencyBarRadius = 0.78;
616 static const FLT gauge_frequencyBarMajorTicRadius = 0.04;
617 static const FLT gauge_okBarStroke = 0.07;
618 static const FLT gauge_okBarRadius = 0.48;
619
620 static const FLT overtureAngle = 65.0 * M_PI / 180.0;
621
622 // colors
623 static const unsigned int gauge_centsBarColor = 0x333355;
624 static const unsigned int gauge_frequencyBarColor = 0x555533;
625 static const unsigned int gauge_okColor = 0x99dd99;
626 static const unsigned int gauge_koColor = 0xddaaaa;
627
628 const int width = gauge_size_x;
629 int height = gauge_size_y;
630
631 // dimensions applied to the current size
632 point_t gaugeCenter = { .x = width / 2, .y = height * gauge_gaugeCenterY };
633
634 if (width < 1.6 * height) {
635 height = width / 1.6;
636 gaugeCenter.y = 0.5 * (gauge_size_y - height)
637 + height * gauge_gaugeCenterY;
638 }
639
640 const FLT centsBarRadius = height * gauge_centsBarRadius;
641 const FLT centsBarStroke = height * gauge_centsBarStroke;
642 const FLT centsBarMajorTicRadius = centsBarRadius
643 - height * gauge_centsBarMajorTicRadius;
644 const FLT centsBarMinorTicRadius = centsBarRadius
645 - height * gauge_centsBarMinorTicRadius;
646 const FLT centsBarMajorTicStroke = height * gauge_centsBarMajorTicStroke;
647 const FLT centsBarMinorTicStroke = height * gauge_centsBarMinorTicStroke;
648 const FLT centsTextSize = height * gauge_centsTextSize;
649 const FLT frequencyBarRadius = height * gauge_frequencyBarRadius;
650 const FLT frequencyBarMajorTicRadius = frequencyBarRadius
651 + height * gauge_frequencyBarMajorTicRadius;
652 const FLT frequencyBarStroke = height * gauge_frequencyBarStroke;
653 const FLT okBarRadius = height * gauge_okBarRadius;
654 const FLT okBarStroke = height * gauge_okBarStroke;
655
656 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
657 cairo_save(cr);
658 const GdkRectangle r = { .x = 0, .y = 0, .width = gauge_size_x, .height =
659 gauge_size_y };
660 gdk_cairo_rectangle(cr, &r);
661 cairo_fill_preserve(cr);
662 cairo_restore(cr);
663 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
664 cairo_stroke(cr);
665
666 // draw ok/ko bar
667 cairo_set_line_width(cr, okBarStroke);
668 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
669 lingot_gui_mainframe_cairo_set_source_argb(cr, gauge_koColor);
670 cairo_arc(cr, gaugeCenter.x, gaugeCenter.y, okBarRadius,
671 -0.5 * M_PI - overtureAngle, -0.5 * M_PI + overtureAngle);
672 cairo_stroke(cr);
673 lingot_gui_mainframe_cairo_set_source_argb(cr, gauge_okColor);
674 cairo_arc(cr, gaugeCenter.x, gaugeCenter.y, okBarRadius,
675 -0.5 * M_PI - 0.1 * overtureAngle,
676 -0.5 * M_PI + 0.1 * overtureAngle);
677 cairo_stroke(cr);
678
679 // draw cents bar
680 cairo_set_line_width(cr, centsBarStroke);
681 lingot_gui_mainframe_cairo_set_source_argb(cr, gauge_centsBarColor);
682 cairo_arc(cr, gaugeCenter.x, gaugeCenter.y, centsBarRadius,
683 -0.5 * M_PI - 1.05 * overtureAngle,
684 -0.5 * M_PI + 1.05 * overtureAngle);
685 cairo_stroke(cr);
686
687 // cent tics
688 const double maxOffsetRounded = frame->conf.scale.max_offset_rounded;
689 static const int maxMinorDivisions = 20;
690 double centsPerMinorDivision = maxOffsetRounded / maxMinorDivisions;
691 const double base = pow(10.0, floor(log10(centsPerMinorDivision)));
692 double normalizedCentsPerDivision = centsPerMinorDivision / base;
693 if (normalizedCentsPerDivision >= 6.0) {
694 normalizedCentsPerDivision = 10.0;
695 } else if (normalizedCentsPerDivision >= 2.5) {
696 normalizedCentsPerDivision = 5.0;
697 } else if (normalizedCentsPerDivision >= 1.2) {
698 normalizedCentsPerDivision = 2.0;
699 } else {
700 normalizedCentsPerDivision = 1.0;
701 }
702 centsPerMinorDivision = normalizedCentsPerDivision * base;
703 const double centsPerMajorDivision = 5.0 * centsPerMinorDivision;
704
705 // minor tics
706 cairo_set_line_width(cr, centsBarMinorTicStroke);
707 int maxIndex = (int) floor(0.5 * maxOffsetRounded / centsPerMinorDivision);
708 double angleStep = 2.0 * overtureAngle * centsPerMinorDivision
709 / maxOffsetRounded;
710 int index;
711 for (index = -maxIndex; index <= maxIndex; index++) {
712 const double angle = index * angleStep;
713 lingot_gui_mainframe_draw_gauge_tic(cr, &gaugeCenter,
714 centsBarMinorTicRadius, centsBarRadius, angle);
715 }
716
717 // major tics
718 maxIndex = (int) floor(0.5 * maxOffsetRounded / centsPerMajorDivision);
719 angleStep = 2.0 * overtureAngle * centsPerMajorDivision / maxOffsetRounded;
720 cairo_set_line_width(cr, centsBarMajorTicStroke);
721 for (index = -maxIndex; index <= maxIndex; index++) {
722 double angle = index * angleStep;
723 lingot_gui_mainframe_draw_gauge_tic(cr, &gaugeCenter,
724 centsBarMajorTicRadius, centsBarRadius, angle);
725 }
726
727 // cents text
728 cairo_set_line_width(cr, 1.0);
729 double oldAngle = 0.0;
730
731 cairo_save(cr);
732
733 static char buff[10];
734
735 cairo_text_extents_t te;
736 cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
737 CAIRO_FONT_WEIGHT_NORMAL);
738 cairo_set_font_size(cr, centsTextSize);
739
740 sprintf(buff, "%s", "cent");
741 cairo_text_extents(cr, buff, &te);
742 cairo_move_to(cr, gaugeCenter.x - te.width / 2 - te.x_bearing,
743 gaugeCenter.y - 0.81 * centsBarMajorTicRadius - te.height / 2
744 - te.y_bearing);
745 cairo_show_text(cr, buff);
746
747 cairo_translate(cr, gaugeCenter.x, gaugeCenter.y);
748 for (index = -maxIndex; index <= maxIndex; index++) {
749 double angle = index * angleStep;
750 cairo_rotate(cr, angle - oldAngle);
751 int cents = (int) (index * centsPerMajorDivision);
752 sprintf(buff, "%s%i", ((cents > 0) ? "+" : ""), cents);
753 cairo_text_extents(cr, buff, &te);
754 cairo_move_to(cr, -te.width / 2 - te.x_bearing,
755 -0.92 * centsBarMajorTicRadius - te.height / 2 - te.y_bearing);
756 cairo_show_text(cr, buff);
757 oldAngle = angle;
758 }
759 cairo_restore(cr);
760 cairo_stroke(cr);
761
762 // draw frequency bar
763 cairo_set_line_width(cr, frequencyBarStroke);
764 lingot_gui_mainframe_cairo_set_source_argb(cr, gauge_frequencyBarColor);
765 cairo_arc(cr, gaugeCenter.x, gaugeCenter.y, frequencyBarRadius,
766 -0.5 * M_PI - 1.05 * overtureAngle,
767 -0.5 * M_PI + 1.05 * overtureAngle);
768 cairo_stroke(cr);
769
770 // frequency tics
771 lingot_gui_mainframe_draw_gauge_tic(cr, &gaugeCenter,
772 frequencyBarMajorTicRadius, frequencyBarRadius, 0.0);
773 }
774
lingot_gui_mainframe_draw_gauge(cairo_t * cr,const LingotMainFrame * frame)775 void lingot_gui_mainframe_draw_gauge(cairo_t *cr, const LingotMainFrame* frame) {
776
777 // normalized dimensions
778 static const FLT gauge_gaugeCenterY = 0.94;
779 static const FLT gauge_gaugeLength = 0.85;
780 static const FLT gauge_gaugeLengthBack = 0.08;
781 static const FLT gauge_gaugeCenterRadius = 0.045;
782 static const FLT gauge_gaugeStroke = 0.012;
783 static const FLT gauge_gaugeShadowOffsetX = 0.015;
784 static const FLT gauge_gaugeShadowOffsetY = 0.01;
785
786 static const FLT overtureAngle = 65.0 * M_PI / 180.0;
787
788 // colors
789 static const unsigned int gauge_gaugeColor = 0xaa3333;
790 static const unsigned int gauge_gaugeShadowColor = 0x88000000;
791
792 const int width = gauge_size_x;
793 int height = gauge_size_y;
794
795 // dimensions applied to the current size
796 point_t gaugeCenter = { .x = width / 2, .y = height * gauge_gaugeCenterY };
797
798 if (width < 1.6 * height) {
799 height = width / 1.6;
800 gaugeCenter.y = 0.5 * (gauge_size_y - height) + height * gauge_gaugeCenterY;
801 }
802
803 const point_t gaugeShadowCenter = { .x = gaugeCenter.x
804 + height * gauge_gaugeShadowOffsetX, .y = gaugeCenter.y
805 + height * gauge_gaugeShadowOffsetY };
806 const FLT gaugeLength = height * gauge_gaugeLength;
807 const FLT gaugeLengthBack = height * gauge_gaugeLengthBack;
808 const FLT gaugeCenterRadius = height * gauge_gaugeCenterRadius;
809 const FLT gaugeStroke = height * gauge_gaugeStroke;
810
811 lingot_gui_mainframe_draw_gauge_background(cr, frame);
812
813 const double normalized_error = frame->gauge.position
814 / frame->conf.scale.max_offset_rounded;
815 const double angle = 2.0 * normalized_error * overtureAngle;
816 cairo_set_line_width(cr, gaugeStroke);
817 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
818 lingot_gui_mainframe_cairo_set_source_argb(cr, gauge_gaugeShadowColor);
819 lingot_gui_mainframe_draw_gauge_tic(cr, &gaugeShadowCenter,
820 -gaugeLengthBack, -0.99 * gaugeCenterRadius, angle);
821 lingot_gui_mainframe_draw_gauge_tic(cr, &gaugeShadowCenter,
822 0.99 * gaugeCenterRadius, gaugeLength, angle);
823 cairo_arc(cr, gaugeShadowCenter.x, gaugeShadowCenter.y, gaugeCenterRadius,
824 0, 2 * M_PI);
825 cairo_fill(cr);
826 lingot_gui_mainframe_cairo_set_source_argb(cr, gauge_gaugeColor);
827 lingot_gui_mainframe_draw_gauge_tic(cr, &gaugeCenter, -gaugeLengthBack,
828 gaugeLength, angle);
829 cairo_arc(cr, gaugeCenter.x, gaugeCenter.y, gaugeCenterRadius, 0, 2 * M_PI);
830 cairo_fill(cr);
831 }
832
lingot_gui_mainframe_get_signal(const LingotMainFrame * frame,int i,FLT min,FLT max)833 FLT lingot_gui_mainframe_get_signal(const LingotMainFrame* frame, int i,
834 FLT min, FLT max) {
835 FLT signal = frame->core.SPL[i];
836 if (signal < min) {
837 signal = min;
838 } else if (signal > max) {
839 signal = max;
840 }
841 return signal - min;
842 }
843
lingot_gui_mainframe_get_noise(const LingotMainFrame * frame,FLT min,FLT max)844 FLT lingot_gui_mainframe_get_noise(const LingotMainFrame* frame, FLT min,
845 FLT max) {
846 FLT noise = frame->conf.min_overall_SNR;
847 if (noise < min) {
848 noise = min;
849 } else if (noise > max) {
850 noise = max;
851 }
852 return noise - min;
853 }
854
lingot_gui_mainframe_format_frequency(FLT freq,char * buff)855 static char* lingot_gui_mainframe_format_frequency(FLT freq, char* buff) {
856 if (freq == 0.0) {
857 sprintf(buff, "0 Hz");
858 } else if (floor(freq) == freq)
859 sprintf(buff, "%0.0f kHz", freq);
860 else if (floor(10 * freq) == 10 * freq) {
861 if (freq <= 1000.0)
862 sprintf(buff, "%0.0f Hz", 1e3 * freq);
863 else
864 sprintf(buff, "%0.1f kHz", freq);
865 } else {
866 if (freq <= 100.0)
867 sprintf(buff, "%0.0f Hz", 1e3 * freq);
868 else
869 sprintf(buff, "%0.2f kHz", freq);
870 }
871
872 return buff;
873 }
874
lingot_gui_mainframe_draw_spectrum_background(cairo_t * cr,const LingotMainFrame * frame)875 void lingot_gui_mainframe_draw_spectrum_background(cairo_t *cr, const LingotMainFrame* frame) {
876
877 const FLT font_size = 8 + spectrum_size_y / 30;
878
879 static char buff[10];
880 static char buff2[10];
881 cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
882 CAIRO_FONT_WEIGHT_NORMAL);
883 cairo_set_font_size(cr, font_size);
884 sprintf(buff, "%0.0f", spectrum_min_db);
885 sprintf(buff2, "%0.0f", spectrum_max_db);
886 cairo_text_extents_t te;
887 cairo_text_extents_t te2;
888 cairo_text_extents(cr, buff, &te);
889 cairo_text_extents(cr, buff2, &te2);
890 if (te2.width > te.width) {
891 te = te2;
892 }
893
894 // spectrum area margins
895 spectrum_bottom_margin = 1.6 * te.height;
896 spectrum_top_margin = spectrum_bottom_margin;
897 spectrum_left_margin = te.width * 1.5;
898 spectrum_right_margin = 0.03 * spectrum_size_x;
899 if (spectrum_right_margin > 0.8 * spectrum_left_margin) {
900 spectrum_right_margin = 0.8 * spectrum_left_margin;
901 }
902 spectrum_inner_x = spectrum_size_x - spectrum_left_margin
903 - spectrum_right_margin;
904 spectrum_inner_y = spectrum_size_y - spectrum_bottom_margin
905 - spectrum_top_margin;
906
907 sprintf(buff, "000 Hz");
908 cairo_text_extents(cr, buff, &te);
909 // minimum grid size in pixels
910 const int minimum_grid_width = 1.5 * te.width;
911 const int minimum_grid_height = 3.0 * te.height;
912
913 // clear all
914 cairo_set_source_rgba(cr, 0.06, 0.2, 0.06, 1.0);
915 GdkRectangle r = { .x = 0, .y = 0, .width = spectrum_size_x, .height =
916 spectrum_size_y };
917 gdk_cairo_rectangle(cr, &r);
918 cairo_fill(cr);
919
920 cairo_set_source_rgba(cr, 0.56, 0.56, 0.56, 1.0);
921 cairo_set_line_width(cr, 1.0);
922 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
923 cairo_move_to(cr, spectrum_left_margin,
924 spectrum_size_y - spectrum_bottom_margin);
925 cairo_rel_line_to(cr, spectrum_inner_x, 0);
926 cairo_stroke(cr);
927
928 // choose scale factor
929 const FLT spectrum_max_frequency = 0.5 * frame->conf.sample_rate
930 / frame->conf.oversampling;
931
932 // scale factors (in KHz) to draw the grid. We will choose the smaller
933 // factor that respects the minimum_grid_width
934 static const double scales[] = { 0.01, 0.05, 0.1, 0.2, 0.5, 1, 2, 4, 11, 22,
935 -1.0 };
936
937 int i;
938 for (i = 0; scales[i + 1] > 0.0; i++) {
939 if ((1e3 * scales[i] * spectrum_inner_x / spectrum_max_frequency)
940 > minimum_grid_width)
941 break;
942 }
943
944 FLT scale = scales[i];
945 FLT grid_width = 1e3 * scales[i] * spectrum_inner_x
946 / spectrum_max_frequency;
947
948 FLT freq = 0.0;
949 FLT x;
950 for (x = 0.0; x <= spectrum_inner_x; x += grid_width) {
951 cairo_move_to(cr, spectrum_left_margin + x, spectrum_top_margin);
952 cairo_rel_line_to(cr, 0, spectrum_inner_y + 3); // TODO: proportion
953 cairo_stroke(cr);
954
955 lingot_gui_mainframe_format_frequency(freq, buff);
956
957 cairo_text_extents(cr, buff, &te);
958 cairo_move_to(cr,
959 spectrum_left_margin + x + 6 - te.width / 2 - te.x_bearing,
960 spectrum_size_y - 0.5 * spectrum_bottom_margin - te.height / 2
961 - te.y_bearing);
962 cairo_show_text(cr, buff);
963 freq += scale;
964 }
965
966 spectrum_db_density = (spectrum_inner_y)
967 / (spectrum_max_db - spectrum_min_db);
968
969 sprintf(buff, "dB");
970
971 cairo_text_extents(cr, buff, &te);
972 cairo_move_to(cr, spectrum_left_margin - te.x_bearing,
973 0.5 * spectrum_top_margin - te.height / 2 - te.y_bearing);
974 cairo_show_text(cr, buff);
975
976 // scale factors (in KHz) to draw the grid. We will choose the smallest
977 // factor that respects the minimum_grid_width
978 static const int db_scales[] = { 5, 10, 20, 25, 50, 75, 100, -1 };
979 for (i = 0; db_scales[i + 1] > 0; i++) {
980 if ((db_scales[i] * spectrum_db_density) > minimum_grid_height)
981 break;
982 }
983
984 const int db_scale = db_scales[i];
985
986 FLT y = 0;
987 int i0 = ceil(spectrum_min_db / db_scale);
988 if (spectrum_min_db < 0.0) {
989 i0--;
990 }
991 int i1 = ceil(spectrum_max_db / db_scale);
992 for (i = i0; i <= i1; i++) {
993 y = spectrum_db_density * (i * db_scale - spectrum_min_db);
994 if ((y < 0.0) || (y > spectrum_inner_y)) {
995 continue;
996 }
997 sprintf(buff, "%d", i * db_scale);
998
999 cairo_text_extents(cr, buff, &te);
1000 cairo_move_to(cr,
1001 0.45 * spectrum_left_margin - te.width / 2 - te.x_bearing,
1002 spectrum_size_y - spectrum_bottom_margin - y - te.height / 2
1003 - te.y_bearing);
1004 cairo_show_text(cr, buff);
1005
1006 cairo_move_to(cr, spectrum_left_margin - 3,
1007 spectrum_size_y - spectrum_bottom_margin - y);
1008 cairo_rel_line_to(cr, spectrum_inner_x + 3, 0);
1009 cairo_stroke(cr);
1010 }
1011 }
1012
lingot_gui_mainframe_draw_spectrum(cairo_t * cr,const LingotMainFrame * frame)1013 void lingot_gui_mainframe_draw_spectrum(cairo_t *cr, const LingotMainFrame* frame) {
1014
1015 unsigned int i;
1016
1017 lingot_gui_mainframe_draw_spectrum_background(cr, frame);
1018
1019 // TODO: change access to frame->core.X
1020 // spectrum drawing.
1021 if (frame->core.running) {
1022
1023 cairo_set_line_width(cr, 1.0);
1024 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
1025
1026 FLT x;
1027 FLT y = -1;
1028
1029 const unsigned int min_index = 0;
1030 const unsigned int max_index = frame->conf.fft_size / 2;
1031
1032 FLT index_density = spectrum_inner_x / max_index;
1033 // TODO: step
1034 const unsigned int index_step = 1;
1035
1036 static const double dashed1[] = { 5.0, 5.0 };
1037 static int len1 = sizeof(dashed1) / sizeof(dashed1[0]);
1038
1039 const FLT x0 = spectrum_left_margin;
1040 const FLT y0 = spectrum_size_y - spectrum_bottom_margin;
1041
1042 FLT dydxm1 = 0;
1043
1044 cairo_set_source_rgba(cr, 0.13, 1.0, 0.13, 1.0);
1045
1046 cairo_translate(cr, x0, y0);
1047 cairo_rectangle(cr, 1.0, -1.0, spectrum_inner_x - 2,
1048 -(spectrum_inner_y - 2));
1049 cairo_clip(cr);
1050 cairo_new_path(cr); // path not consumed by clip()
1051
1052 y = -spectrum_db_density
1053 * lingot_gui_mainframe_get_signal(frame, min_index,
1054 spectrum_min_db, spectrum_max_db); // dB.
1055
1056 cairo_move_to(cr, 0, 0);
1057 cairo_line_to(cr, 0, y);
1058
1059 FLT yp1 = -spectrum_db_density
1060 * lingot_gui_mainframe_get_signal(frame, min_index + 1,
1061 spectrum_min_db, spectrum_max_db);
1062 FLT ym1 = y;
1063
1064 for (i = index_step; i < max_index - 1; i += index_step) {
1065
1066 x = index_density * i;
1067 ym1 = y;
1068 y = yp1;
1069 yp1 = -spectrum_db_density
1070 * lingot_gui_mainframe_get_signal(frame, i + 1,
1071 spectrum_min_db, spectrum_max_db);
1072 FLT dydx = (yp1 - ym1) / (2 * index_density);
1073 static const FLT dx = 0.4;
1074 FLT x1 = x - (1 - dx) * index_density;
1075 FLT x2 = x - dx * index_density;
1076 FLT y1 = ym1 + dydxm1 * dx;
1077 FLT y2 = y - dydx * dx;
1078
1079 dydxm1 = dydx;
1080 cairo_curve_to(cr, x1, y1, x2, y2, x, y);
1081 // cairo_line_to(cr, x, y);
1082 }
1083
1084 y = -spectrum_db_density
1085 * lingot_gui_mainframe_get_signal(frame, max_index - 1,
1086 spectrum_min_db, spectrum_max_db); // dB.
1087 cairo_line_to(cr, index_density * max_index, y);
1088 cairo_line_to(cr, index_density * max_index, 0);
1089 cairo_close_path(cr);
1090 cairo_fill_preserve(cr);
1091 // cairo_restore(cr);
1092 cairo_stroke(cr);
1093
1094 #ifdef DRAW_MARKERS
1095 cairo_set_source_rgba(cr, 1.0, 1.0, 0.13, 1.0);
1096 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1097 cairo_set_line_width(cr, 10.0);
1098
1099 for (i = 0; i < frame->core.markers_size2; i++) {
1100
1101 x = index_density * frame->core.markers2[i];
1102 y = -spectrum_db_density
1103 * lingot_gui_mainframe_get_signal(frame,
1104 frame->core.markers2[i], spectrum_min_db,
1105 spectrum_max_db); // dB.
1106 cairo_move_to(cr, x, y);
1107 cairo_rel_line_to(cr, 0, 0);
1108 cairo_stroke(cr);
1109 }
1110
1111 cairo_set_line_width(cr, 4.0);
1112 cairo_set_source_rgba(cr, 0.13, 0.13, 1.0, 1.0);
1113
1114 for (i = 0; i < frame->core.markers_size; i++) {
1115
1116 x = index_density * frame->core.markers[i];
1117 y = -spectrum_db_density
1118 * lingot_gui_mainframe_get_signal(frame,
1119 frame->core.markers[i], spectrum_min_db,
1120 spectrum_max_db); // dB.
1121 cairo_move_to(cr, x, y);
1122 cairo_rel_line_to(cr, 0, 0);
1123 cairo_stroke(cr);
1124 }
1125 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
1126 cairo_set_line_width(cr, 1.0);
1127 #endif
1128
1129 if (frame->core.freq != 0.0) {
1130
1131 cairo_set_dash(cr, dashed1, len1, 0);
1132
1133 // fundamental frequency mark with a red vertical line.
1134 cairo_set_source_rgba(cr, 1.0, 0.13, 0.13, 1.0);
1135 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1136
1137 cairo_set_line_width(cr, 1.0);
1138
1139 // index of closest sample to fundamental frequency.
1140 x = index_density * frame->core.freq * frame->conf.fft_size
1141 * frame->conf.oversampling / frame->conf.sample_rate;
1142 cairo_move_to(cr, x, 0);
1143 cairo_rel_line_to(cr, 0.0, -spectrum_inner_y);
1144 cairo_stroke(cr);
1145
1146 // i = (int) rint(
1147 // frame->core.freq * frame->conf.fft_size
1148 // * frame->conf.oversampling
1149 // / frame->conf.sample_rate);
1150 // y = -spectrum_db_density
1151 // * lingot_gui_mainframe_get_signal(frame, i, spectrum_min_db,
1152 // spectrum_max_db); // dB.
1153 // cairo_set_line_width(cr, 4.0);
1154 // cairo_move_to(cr, x, y);
1155 // cairo_rel_line_to(cr, 0, 0);
1156 // cairo_stroke(cr);
1157
1158 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
1159 cairo_set_line_width(cr, 1.0);
1160 }
1161
1162 cairo_set_dash(cr, dashed1, len1, 0);
1163 cairo_set_source_rgba(cr, 1.0, 1.0, 0.2, 1.0);
1164
1165 y = -spectrum_db_density
1166 * lingot_gui_mainframe_get_noise(frame, spectrum_min_db,
1167 spectrum_max_db); // dB.
1168 cairo_move_to(cr, 0, y);
1169 // noise threshold drawing.
1170 for (i = min_index + index_step; i < max_index - 1; i += index_step) {
1171
1172 x = index_density * i;
1173 y = -spectrum_db_density
1174 * lingot_gui_mainframe_get_noise(frame, spectrum_min_db,
1175 spectrum_max_db); // dB.
1176 cairo_line_to(cr, x, y);
1177 }
1178 cairo_stroke(cr);
1179
1180 }
1181
1182 }
1183
lingot_gui_mainframe_draw_labels(const LingotMainFrame * frame)1184 void lingot_gui_mainframe_draw_labels(const LingotMainFrame* frame) {
1185
1186 char* note_string;
1187 static char error_string[30];
1188 static char freq_string[30];
1189 static char octave_string[30];
1190
1191 // draw note, error and frequency labels
1192
1193 if (frequency == 0.0) {
1194 note_string = "---";
1195 strcpy(error_string, "e = ---");
1196 strcpy(freq_string, "f = ---");
1197 strcpy(octave_string, "");
1198 } else {
1199 note_string =
1200 frame->conf.scale.note_name[lingot_config_scale_get_note_index(
1201 &frame->conf.scale, closest_note_index)];
1202 sprintf(error_string, "e = %+2.0f cents", frame->gauge.position);
1203 sprintf(freq_string, "f = %6.2f Hz", frequency);
1204 sprintf(octave_string, "%d",
1205 lingot_config_scale_get_octave(&frame->conf.scale,
1206 closest_note_index) + 4);
1207 }
1208
1209 int font_size = 9 + labelsbox_size_x / 80;
1210 char* markup = g_markup_printf_escaped("<span font_desc=\"%d\">%s</span>",
1211 font_size, freq_string);
1212 gtk_label_set_markup(GTK_LABEL(frame->freq_label), markup);
1213 g_free(markup);
1214 markup = g_markup_printf_escaped("<span font_desc=\"%d\">%s</span>",
1215 font_size, error_string);
1216 gtk_label_set_markup(GTK_LABEL(frame->error_label), markup);
1217 g_free(markup);
1218
1219 font_size = 10 + labelsbox_size_x / 22;
1220 markup =
1221 g_markup_printf_escaped(
1222 "<span font_desc=\"%d\" weight=\"bold\">%s</span><span font_desc=\"%d\" weight=\"bold\"><sub>%s</sub></span>",
1223 font_size, note_string, (int) (0.75 * font_size),
1224 octave_string);
1225 gtk_label_set_markup(GTK_LABEL(frame->tone_label), markup);
1226 g_free(markup);
1227 }
1228
lingot_gui_mainframe_change_config(LingotMainFrame * frame,LingotConfig * conf)1229 void lingot_gui_mainframe_change_config(LingotMainFrame* frame,
1230 LingotConfig* conf) {
1231 lingot_core_stop(&frame->core);
1232 lingot_core_destroy(&frame->core);
1233
1234 // dup.
1235 lingot_config_copy(&frame->conf, conf);
1236
1237 lingot_core_new(&frame->core, &frame->conf);
1238 lingot_core_start(&frame->core);
1239
1240 // some parameters may have changed
1241 lingot_config_copy(conf, &frame->conf);
1242 }
1243