1 // FLTK GUI related functions
2 //
3 // Copyright 2007-2018 by Daniel Noethen.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or (at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <time.h>
21 #include <sys/stat.h>
22
23 #ifndef WIN32
24 #include <sys/wait.h>
25 #endif
26
27 #include "gettext.h"
28 #include "config.h"
29
30 #include "cfg.h"
31 #include "butt.h"
32 #include "util.h"
33 #include "port_audio.h"
34 #include "timer.h"
35 #include "flgui.h"
36 #include "fl_funcs.h"
37 #include "shoutcast.h"
38 #include "icecast.h"
39 #include "strfuncs.h"
40
41
fill_cfg_widgets(void)42 void fill_cfg_widgets(void)
43 {
44 int i;
45
46 int bitrate[] = { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112,
47 128, 160, 192, 224, 256, 320, 0 };
48
49 #ifndef HAVE_LIBFDK_AAC
50 fl_g->menu_item_cfg_aac->hide();
51 fl_g->menu_item_rec_aac->hide();
52 Fl::check();
53 #endif
54
55 //fill the main section
56 for(i = 0; i < cfg.audio.dev_count; i++)
57 fl_g->choice_cfg_dev->add(cfg.audio.pcm_list[i]->name);
58
59 fl_g->choice_cfg_dev->value(cfg.audio.dev_num);
60
61 fl_g->choice_cfg_act_srv->clear();
62 fl_g->choice_cfg_act_srv->redraw();
63 for(i = 0; i < cfg.main.num_of_srv; i++)
64 fl_g->choice_cfg_act_srv->add(cfg.srv[i]->name);
65
66 if(cfg.main.num_of_srv > 0)
67 {
68 fl_g->button_cfg_edit_srv->activate();
69 fl_g->button_cfg_del_srv->activate();
70 fl_g->choice_cfg_act_srv->activate();
71 }
72 else
73 {
74 fl_g->button_cfg_edit_srv->deactivate();
75 fl_g->button_cfg_del_srv->deactivate();
76 fl_g->choice_cfg_act_srv->deactivate();
77 }
78
79 fl_g->choice_cfg_act_icy->clear();
80 fl_g->choice_cfg_act_icy->redraw();
81 for(i = 0; i < cfg.main.num_of_icy; i++)
82 fl_g->choice_cfg_act_icy->add(cfg.icy[i]->name);
83
84 if(cfg.main.num_of_icy > 0)
85 {
86 fl_g->button_cfg_edit_icy->activate();
87 fl_g->button_cfg_del_icy->activate();
88 fl_g->choice_cfg_act_icy->activate();
89
90 }
91 else
92 {
93 fl_g->button_cfg_edit_icy->deactivate();
94 fl_g->button_cfg_del_icy->deactivate();
95 fl_g->choice_cfg_act_icy->deactivate();
96 }
97
98 fl_g->choice_cfg_act_srv->value(cfg.selected_srv);
99 fl_g->choice_cfg_act_icy->value(cfg.selected_icy);
100
101
102 fl_g->check_cfg_connect->value(cfg.main.connect_at_startup);
103 fl_g->check_cfg_force_reconnecting->value(cfg.main.force_reconnecting);
104
105 fl_g->input_log_filename->value(cfg.main.log_file);
106
107 fl_g->check_update_at_startup->value(cfg.main.check_for_update);
108
109 //fill the audio section
110 if(!strcmp(cfg.audio.codec, "mp3"))
111 fl_g->choice_cfg_codec->value(CHOICE_MP3);
112 else if(!strcmp(cfg.audio.codec, "ogg"))
113 fl_g->choice_cfg_codec->value(CHOICE_OGG);
114 else if(!strcmp(cfg.audio.codec, "opus"))
115 fl_g->choice_cfg_codec->value(CHOICE_OPUS);
116 else if(!strcmp(cfg.audio.codec, "aac"))
117 fl_g->choice_cfg_codec->value(CHOICE_AAC);
118 else if(!strcmp(cfg.audio.codec, "flac"))
119 {
120 fl_g->choice_cfg_codec->value(CHOICE_FLAC);
121 fl_g->choice_cfg_bitrate->hide();
122
123 }
124
125 if(cfg.audio.channel == 1)
126 fl_g->choice_cfg_channel->value(CHOICE_MONO);
127 else
128 fl_g->choice_cfg_channel->value(CHOICE_STEREO);
129
130 for(i = 0; bitrate[i] != 0; i++)
131 {
132 if(cfg.audio.bitrate == bitrate[i])
133 {
134 fl_g->choice_cfg_bitrate->value(i);
135 break;
136 }
137 }
138
139 fl_g->input_cfg_present_level->value(-cfg.audio.signal_level);
140 fl_g->input_cfg_absent_level->value(-cfg.audio.silence_level);
141
142 fl_g->check_cfg_mono_to_stereo->value(cfg.audio.mono_to_stereo);
143
144 fl_g->input_cfg_buffer->value(cfg.audio.buffer_ms);
145
146 fl_g->choice_cfg_resample_mode->value(cfg.audio.resample_mode);
147
148 // Fill stream section
149 fl_g->check_song_update_active->value(cfg.main.song_update);
150 fl_g->check_read_last_line->value(cfg.main.read_last_line);
151
152 fl_g->input_cfg_song_file->value(cfg.main.song_path);
153 fl_g->input_cfg_signal->value(cfg.main.signal_threshold);
154 fl_g->input_cfg_silence->value(cfg.main.silence_threshold);
155
156 fl_g->input_cfg_song_prefix->value(cfg.main.song_prefix);
157 fl_g->input_cfg_song_suffix->value(cfg.main.song_suffix);
158
159
160 #if __APPLE__ && __MACH__
161 fl_g->choice_cfg_app->add("iTunes\\/Music");
162 fl_g->choice_cfg_app->add("Spotify");
163 fl_g->choice_cfg_app->add("VOX");
164 fl_g->check_cfg_use_app->value(cfg.main.app_update);
165 fl_g->choice_cfg_app->value(cfg.main.app_update_service);
166 #elif (__linux__ || __FreeBSD__) && HAVE_DBUS
167 fl_g->choice_cfg_app->add("Rhythmbox");
168 fl_g->choice_cfg_app->add("Banshee");
169 fl_g->choice_cfg_app->add("Clementine");
170 fl_g->choice_cfg_app->add("Spotify");
171 fl_g->choice_cfg_app->add("Cantata");
172 fl_g->choice_cfg_app->add("Strawberry");
173 fl_g->check_cfg_use_app->value(cfg.main.app_update);
174 fl_g->choice_cfg_app->value(cfg.main.app_update_service);
175 #elif WIN32
176 fl_g->choice_cfg_app->add(_("Not supported on Windows"));
177 fl_g->choice_cfg_app->value(0);
178 fl_g->check_cfg_use_app->value(0);
179 fl_g->check_cfg_use_app->deactivate();
180 fl_g->choice_cfg_app->deactivate();
181 #endif
182
183
184
185 //fill the record section
186 fl_g->input_rec_filename->value(cfg.rec.filename);
187 fl_g->input_rec_folder->value(cfg.rec.folder);
188 fl_g->input_rec_split_time->value(cfg.rec.split_time);
189
190 if(!strcmp(cfg.rec.codec, "mp3"))
191 fl_g->choice_rec_codec->value(CHOICE_MP3);
192 else if(!strcmp(cfg.rec.codec, "ogg"))
193 fl_g->choice_rec_codec->value(CHOICE_OGG);
194 else if(!strcmp(cfg.rec.codec, "opus"))
195 fl_g->choice_rec_codec->value(CHOICE_OPUS);
196 else if(!strcmp(cfg.rec.codec, "aac"))
197 fl_g->choice_rec_codec->value(CHOICE_AAC);
198 else if(!strcmp(cfg.rec.codec, "flac"))
199 {
200 fl_g->choice_rec_codec->value(CHOICE_FLAC);
201 fl_g->choice_rec_bitrate->hide();
202 }
203 else //wav
204 {
205 fl_g->choice_rec_codec->value(CHOICE_WAV);
206 fl_g->choice_rec_bitrate->hide();
207 }
208
209 for(i = 0; bitrate[i] != 0; i++)
210 if(cfg.rec.bitrate == bitrate[i])
211 fl_g->choice_rec_bitrate->value(i);
212
213 if(cfg.rec.start_rec)
214 fl_g->check_cfg_auto_start_rec->value(1);
215 else
216 fl_g->check_cfg_auto_start_rec->value(0);
217
218 if(cfg.rec.stop_rec)
219 fl_g->check_cfg_auto_stop_rec->value(1);
220 else
221 fl_g->check_cfg_auto_stop_rec->value(0);
222
223
224 if(cfg.rec.rec_after_launch)
225 fl_g->check_cfg_rec_after_launch->value(1);
226 else
227 fl_g->check_cfg_rec_after_launch->value(0);
228
229 if(cfg.rec.sync_to_hour)
230 fl_g->check_sync_to_full_hour->value(1);
231 else
232 fl_g->check_sync_to_full_hour->value(0);
233
234 fl_g->input_rec_signal->value(cfg.rec.signal_threshold);
235 fl_g->input_rec_silence->value(cfg.rec.silence_threshold);
236
237 //fill the ssl/tls section
238 fl_g->input_tls_cert_file->value(cfg.tls.cert_file);
239 fl_g->input_tls_cert_dir->value(cfg.tls.cert_dir);
240
241
242 update_samplerates();
243 update_channel_lists();
244
245 //fill the DSP section
246 fl_g->check_activate_eq->value(cfg.dsp.equalizer);
247
248 slider_equalizer1_cb(cfg.dsp.gain1);
249 fl_g->equalizerSlider1->value(cfg.dsp.gain1);
250
251 slider_equalizer2_cb(cfg.dsp.gain2);
252 fl_g->equalizerSlider2->value(cfg.dsp.gain2);
253
254 slider_equalizer3_cb(cfg.dsp.gain3);
255 fl_g->equalizerSlider3->value(cfg.dsp.gain3);
256
257 slider_equalizer4_cb(cfg.dsp.gain4);
258 fl_g->equalizerSlider4->value(cfg.dsp.gain4);
259
260 slider_equalizer5_cb(cfg.dsp.gain5);
261 fl_g->equalizerSlider5->value(cfg.dsp.gain5);
262
263 fl_g->check_activate_drc->value(cfg.dsp.compressor);
264
265 slider_threshold_cb(cfg.dsp.threshold);
266 fl_g->thresholdSlider->value(cfg.dsp.threshold);
267
268 slider_ratio_cb(cfg.dsp.ratio);
269 fl_g->ratioSlider->value(cfg.dsp.ratio);
270
271 slider_attack_cb(cfg.dsp.attack);
272 fl_g->attackSlider->value(cfg.dsp.attack);
273
274 slider_release_cb(cfg.dsp.release);
275 fl_g->releaseSlider->value(cfg.dsp.release);
276
277 slider_makeup_cb(cfg.dsp.makeup_gain);
278 fl_g->makeupSlider->value(cfg.dsp.makeup_gain);
279
280 //fill the GUI section
281 fl_g->button_gui_bg_color->color(cfg.main.bg_color,
282 fl_lighter((Fl_Color)cfg.main.bg_color));
283 fl_g->button_gui_text_color->color(cfg.main.txt_color,
284 fl_lighter((Fl_Color)cfg.main.txt_color));
285 fl_g->check_gui_attach->value(cfg.gui.attach);
286 fl_g->check_gui_ontop->value(cfg.gui.ontop);
287 if(cfg.gui.ontop)
288 {
289 fl_g->window_main->stay_on_top(1);
290 fl_g->window_cfg->stay_on_top(1);
291 }
292 fl_g->check_gui_hide_log_window->value(cfg.gui.hide_log_window);
293 fl_g->check_gui_remember_pos->value(cfg.gui.remember_pos);
294
295
296 fl_g->check_gui_lcd_auto->value(cfg.gui.lcd_auto);
297
298 fl_g->choice_gui_language->value(cfg.gui.lang);
299
300 }
301
302 //Updates the samplerate drop down menu for the audio
303 //device the user has selected
update_samplerates(void)304 void update_samplerates(void)
305 {
306 int i;
307 int *sr_list;
308 char sr_asc[10];
309
310 fl_g->choice_cfg_samplerate->clear();
311
312 sr_list = cfg.audio.pcm_list[cfg.audio.dev_num]->sr_list;
313
314 for(i = 0; sr_list[i] != 0; i++)
315 {
316 snprintf(sr_asc, sizeof(sr_asc), "%dHz", sr_list[i]);
317 fl_g->choice_cfg_samplerate->add(sr_asc);
318
319 if(cfg.audio.samplerate == sr_list[i])
320 fl_g->choice_cfg_samplerate->value(i);
321 }
322 if(i == 0)
323 {
324 fl_g->choice_cfg_samplerate->add("dev. not supported");
325 fl_g->choice_cfg_samplerate->value(0);
326 }
327 }
328
update_channel_lists(void)329 void update_channel_lists(void)
330 {
331
332 int i;
333 char ch_num_txt[8];
334
335 int dev_num = fl_g->choice_cfg_dev->value();
336 char* dev_name = cfg.audio.pcm_list[dev_num]->name;
337 int num_of_channels = cfg.audio.pcm_list[dev_num]->num_of_channels;
338
339 fl_g->choice_cfg_left_channel->clear();
340 fl_g->choice_cfg_right_channel->clear();
341
342 for (i = 1; i <= num_of_channels; i++)
343 {
344 snprintf(ch_num_txt, sizeof(ch_num_txt), "%d", i);
345 fl_g->choice_cfg_left_channel->add(ch_num_txt);
346 fl_g->choice_cfg_right_channel->add(ch_num_txt);
347 }
348
349 fl_g->choice_cfg_left_channel->value(cfg.audio.left_ch-1);
350 fl_g->choice_cfg_right_channel->value(cfg.audio.right_ch-1);
351
352 }
353
print_info(const char * info,int info_type)354 void print_info(const char* info, int info_type)
355 {
356 char timebuf[10];
357 char logtimestamp[21];
358 char* infotxt;
359 FILE *log_fd;
360 int len;
361
362 time_t test;
363 struct tm *mytime;
364 static struct tm time_bak;
365
366 infotxt = strdup(info);
367
368
369 test = time(NULL);
370 mytime = localtime(&test);
371
372 if( (time_bak.tm_min != mytime->tm_min) || (time_bak.tm_hour != mytime->tm_hour) )
373 {
374 time_bak.tm_min = mytime->tm_min;
375 time_bak.tm_hour = mytime->tm_hour;
376 strftime(timebuf, sizeof(timebuf), "\n%H:%M:", mytime);
377 fl_g->info_buffer->append(timebuf);
378 }
379
380 fl_g->info_buffer->append((const char*)"\n");
381 fl_g->info_buffer->append((const char*)info);
382
383 //always scroll to the last line
384 fl_g->info_output->scroll(fl_g->info_buffer->count_lines(0, //count the lines from char 0 to the last character
385 fl_g->info_buffer->length()), //returns the number of characters in the buffer
386 0);
387
388 // log to log_file if defined
389 if ((cfg.main.log_file != NULL) && (strlen(cfg.main.log_file) > 0))
390 {
391 strftime(logtimestamp, sizeof(logtimestamp), "%Y-%m-%d %H:%M:%S", mytime);
392 log_fd = fl_fopen(cfg.main.log_file, "ab");
393 if (log_fd != NULL)
394 {
395 if(strchr(infotxt, ':'))
396 strrpl(&infotxt, (char*)"\n", (char*)", ", MODE_ALL);
397 else
398 strrpl(&infotxt, (char*)"\n", (char*)" ", MODE_ALL);
399
400 strrpl(&infotxt, (char*)":,", (char*)": ", MODE_ALL);
401 strrpl(&infotxt, (char*)"\t", (char*)"", MODE_ALL);
402
403 len = strlen(infotxt)-1;
404 // remove trailing commas and spaces
405 while (infotxt[len] == ',' || infotxt[len] == ' ')
406 {
407 infotxt[len--] = '\0';
408 }
409
410 fprintf(log_fd, "%s %s\n", logtimestamp, infotxt);
411 fclose(log_fd);
412 }
413
414 }
415
416 free(infotxt);
417 }
418
print_lcd(const char * text,int len,int home,int clear)419 void print_lcd(const char *text, int len, int home, int clear)
420 {
421 if(clear)
422 fl_g->lcd->clear();
423
424 fl_g->lcd->print((const uchar*)text, len);
425
426 if(home)
427 fl_g->lcd->cursor_pos(0);
428 }
429
430
expand_string(char ** str)431 void expand_string(char **str)
432 {
433 char m[3], d[3], y[5], hh[3], mi[3], ss[3];
434 struct tm *date;
435 const time_t t = time(NULL);
436
437 date = localtime(&t);
438
439 snprintf(ss, 3, "%02d", date->tm_sec);
440 snprintf(mi, 3, "%02d", date->tm_min);
441 snprintf(hh, 3, "%02d", date->tm_hour);
442 snprintf(d, 3, "%02d", date->tm_mday);
443 snprintf(m, 3, "%02d", date->tm_mon+1);
444 snprintf(y, 5, "%d", date->tm_year+1900);
445
446 strrpl(str, (char*)"%S", ss, MODE_ALL);
447 strrpl(str, (char*)"%M", mi, MODE_ALL);
448 strrpl(str, (char*)"%H", hh, MODE_ALL);
449 strrpl(str, (char*)"%d", d, MODE_ALL);
450 strrpl(str, (char*)"%m", m, MODE_ALL);
451 strrpl(str, (char*)"%y", y, MODE_ALL);
452 strrpl(str, (char*)"%Y", y, MODE_ALL); // %Y and %y both work as year
453 }
454
test_file_extension(void)455 void test_file_extension(void)
456 {
457 char *current_ext;
458
459 current_ext = util_get_file_extension(cfg.rec.filename);
460
461 // Append extension
462 if(current_ext == NULL)
463 {
464 cfg.rec.filename = (char*)realloc(cfg.rec.filename, strlen(cfg.rec.filename)+strlen(cfg.rec.codec)+2);
465 strcat(cfg.rec.filename, ".");
466 strcat(cfg.rec.filename, cfg.rec.codec);
467 fl_g->input_rec_filename->value(cfg.rec.filename);
468 }
469 // Replace extension
470 else if(strcmp(current_ext, cfg.rec.codec))
471 {
472 strrpl(&cfg.rec.filename, current_ext, cfg.rec.codec, MODE_LAST);
473 fl_g->input_rec_filename->value(cfg.rec.filename);
474 }
475 }
476
477
478
init_main_gui_and_audio(void)479 void init_main_gui_and_audio(void)
480 {
481 if(cfg.gui.remember_pos)
482 fl_g->window_main->position(cfg.gui.x_pos, cfg.gui.y_pos);
483
484 fl_g->slider_gain->value(util_factor_to_db(cfg.main.gain));
485 fl_g->window_main->redraw();
486
487 if(cfg.gui.ontop)
488 fl_g->window_main->stay_on_top(1);
489
490 fl_g->button_info->label(_("Hide log"));
491 if(cfg.gui.hide_log_window)
492 button_info_cb();
493
494
495 lame_stream.channel = cfg.audio.channel;
496 lame_stream.bitrate = cfg.audio.bitrate;
497 lame_stream.samplerate = cfg.audio.samplerate;
498 lame_enc_reinit(&lame_stream);
499
500 lame_rec.channel = cfg.audio.channel;
501 lame_rec.bitrate = cfg.rec.bitrate;
502 lame_rec.samplerate = cfg.audio.samplerate;
503 lame_enc_reinit(&lame_rec);
504
505 vorbis_stream.channel = cfg.audio.channel;
506 vorbis_stream.bitrate = cfg.audio.bitrate;
507 vorbis_stream.samplerate = cfg.audio.samplerate;
508 vorbis_enc_reinit(&vorbis_stream);
509
510 vorbis_rec.channel = cfg.audio.channel;
511 vorbis_rec.bitrate = cfg.rec.bitrate;
512 vorbis_rec.samplerate = cfg.audio.samplerate;
513 vorbis_enc_reinit(&vorbis_rec);
514
515 opus_stream.channel = cfg.audio.channel;
516 opus_stream.bitrate = cfg.audio.bitrate*1000;
517 opus_stream.samplerate = cfg.audio.samplerate;
518 opus_enc_reinit(&opus_stream);
519
520 opus_rec.channel = cfg.audio.channel;
521 opus_rec.bitrate = cfg.rec.bitrate*1000;
522 opus_rec.samplerate = cfg.audio.samplerate;
523 opus_enc_reinit(&opus_rec);
524
525 #ifdef HAVE_LIBFDK_AAC
526 if (g_aac_lib_available == 1)
527 {
528 aac_stream.channel = cfg.audio.channel;
529 aac_stream.bitrate = cfg.audio.bitrate;
530 aac_stream.samplerate = cfg.audio.samplerate;
531 aac_stream.aot = cfg.audio.aac_aot;
532 aac_enc_reinit(&aac_stream);
533
534 aac_rec.channel = cfg.audio.channel;
535 aac_rec.bitrate = cfg.rec.bitrate;
536 aac_rec.samplerate = cfg.audio.samplerate;
537 aac_rec.aot = cfg.audio.aac_aot;
538 aac_enc_reinit(&aac_rec);
539 }
540 #endif
541
542 flac_stream.channel = cfg.audio.channel;
543 flac_stream.samplerate = cfg.audio.samplerate;
544 flac_stream.enc_type = FLAC_ENC_TYPE_STREAM;
545 flac_enc_reinit(&flac_stream);
546
547 flac_rec.channel = cfg.audio.channel;
548 flac_rec.samplerate = cfg.audio.samplerate;
549 flac_rec.enc_type = FLAC_ENC_TYPE_REC;
550 flac_enc_reinit(&flac_rec);
551 }
552
553
554 char *ask_user_msg = NULL;
555 char *ask_user_hash = NULL;
556 int ask_user_has_clicked = 0;
557 int ask_user_answer = 0;
558
ask_user_reset(void)559 void ask_user_reset(void)
560 {
561 if (ask_user_msg != NULL)
562 {
563 free(ask_user_msg);
564 ask_user_msg = NULL;
565 }
566 if (ask_user_hash != NULL)
567 {
568 free(ask_user_hash);
569 ask_user_hash = NULL;
570 }
571 ask_user_has_clicked = 0;
572 }
573
ask_user_get_has_clicked(void)574 int ask_user_get_has_clicked(void)
575 {
576 return ask_user_has_clicked;
577 }
578
ask_user_get_answer(void)579 int ask_user_get_answer(void)
580 {
581 return ask_user_answer;
582 }
583
ask_user_set_msg(char * msg)584 void ask_user_set_msg(char *msg)
585 {
586 int len = strlen(msg);
587 ask_user_msg = (char*)calloc(len+1, sizeof(char));
588 strncpy(ask_user_msg, msg, len);
589 }
590
ask_user_set_hash(char * hash)591 void ask_user_set_hash(char *hash)
592 {
593 int len = strlen(hash);
594 ask_user_hash = (char*)calloc(len+1, sizeof(char));
595 strncpy(ask_user_hash, hash, len);
596 }
597
ask_user_ask(void)598 void ask_user_ask(void)
599 {
600 if (fl_choice("%s", _("TRUST"), _("No"), NULL, ask_user_msg) == 1)
601 { // No
602 ask_user_answer = IC_ABORT;
603 }
604 else
605 { // TRUST
606 int len = strlen(ask_user_hash);
607 cfg.srv[cfg.selected_srv]->cert_hash =
608 (char*)realloc(cfg.srv[cfg.selected_srv]->cert_hash, len+1);
609
610 memset(cfg.srv[cfg.selected_srv]->cert_hash, 0, len+1);
611 strncpy(cfg.srv[cfg.selected_srv]->cert_hash, ask_user_hash, len);
612 ask_user_answer = IC_RETRY;
613 }
614
615 ask_user_has_clicked = 1;
616 }
617