1 #include <locale.h>
2 
3 #ifdef HAVE_ALLOCA_H
4 #include <alloca.h>
5 #else
6 #include <stdlib.h>
7 #endif
8 
9 #include <fcntl.h>
10 #include <math.h>
11 
12 #ifndef M_PI
13 #define M_PI 3.1415926535897932385
14 #endif
15 
16 #include <stdbool.h>
17 #include <stddef.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <termios.h>
21 
22 #include <ctype.h>
23 #include <dirent.h>
24 #include <fftw3.h>
25 #include <getopt.h>
26 #include <pthread.h>
27 #include <signal.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <time.h>
33 #include <unistd.h>
34 
35 #include "debug.h"
36 #include "util.h"
37 
38 #ifdef NCURSES
39 #include "output/terminal_ncurses.h"
40 #include <curses.h>
41 #endif
42 
43 #include "output/raw.h"
44 
45 #include "input/alsa.h"
46 #include "input/common.h"
47 #include "input/fifo.h"
48 #include "input/portaudio.h"
49 #include "input/pulse.h"
50 #include "input/shmem.h"
51 #include "input/sndio.h"
52 
53 #include "config.h"
54 
55 #ifdef __GNUC__
56 // curses.h or other sources may already define
57 #undef GCC_UNUSED
58 #define GCC_UNUSED __attribute__((unused))
59 #else
60 #define GCC_UNUSED /* nothing */
61 #endif
62 
63 pthread_mutex_t lock;
64 // used by sig handler
65 // needs to know output mode in order to clean up terminal
66 int output_mode;
67 // whether we should reload the config or not
68 int should_reload = 0;
69 // whether we should only reload colors or not
70 int reload_colors = 0;
71 // whether we should quit
72 int should_quit = 0;
73 
74 // these variables are used only in main, but making them global
75 // will allow us to not free them on exit without ASan complaining
76 struct config_params p;
77 
78 fftw_complex *out_bass_l, *out_bass_r;
79 fftw_plan p_bass_l, p_bass_r;
80 fftw_complex *out_mid_l, *out_mid_r;
81 fftw_plan p_mid_l, p_mid_r;
82 fftw_complex *out_treble_l, *out_treble_r;
83 fftw_plan p_treble_l, p_treble_r;
84 
85 // general: cleanup
cleanup(void)86 void cleanup(void) {
87     if (output_mode == OUTPUT_NCURSES) {
88 #ifdef NCURSES
89         cleanup_terminal_ncurses();
90 #else
91         ;
92 #endif
93     }
94 }
95 
96 // general: handle signals
sig_handler(int sig_no)97 void sig_handler(int sig_no) {
98     if (sig_no == SIGUSR1) {
99         should_reload = 1;
100         return;
101     }
102 
103     if (sig_no == SIGUSR2) {
104         reload_colors = 1;
105         return;
106     }
107 
108     cleanup();
109     if (sig_no == SIGINT) {
110         printf("CTRL-C pressed -- goodbye\n");
111     }
112     signal(sig_no, SIG_DFL);
113     raise(sig_no);
114 }
115 
116 #ifdef ALSA
is_loop_device_for_sure(const char * text)117 static bool is_loop_device_for_sure(const char *text) {
118     const char *const LOOPBACK_DEVICE_PREFIX = "hw:Loopback,";
119     return strncmp(text, LOOPBACK_DEVICE_PREFIX, strlen(LOOPBACK_DEVICE_PREFIX)) == 0;
120 }
121 
directory_exists(const char * path)122 static bool directory_exists(const char *path) {
123     DIR *const dir = opendir(path);
124     if (dir == NULL)
125         return false;
126 
127     closedir(dir);
128     return true;
129 }
130 
131 #endif
132 
monstercat_filter(int * bars,int number_of_bars,int waves,double monstercat)133 int *monstercat_filter(int *bars, int number_of_bars, int waves, double monstercat) {
134 
135     int z;
136 
137     // process [smoothing]: monstercat-style "average"
138 
139     int m_y, de;
140     if (waves > 0) {
141         for (z = 0; z < number_of_bars; z++) { // waves
142             bars[z] = bars[z] / 1.25;
143             // if (bars[z] < 1) bars[z] = 1;
144             for (m_y = z - 1; m_y >= 0; m_y--) {
145                 de = z - m_y;
146                 bars[m_y] = max(bars[z] - pow(de, 2), bars[m_y]);
147             }
148             for (m_y = z + 1; m_y < number_of_bars; m_y++) {
149                 de = m_y - z;
150                 bars[m_y] = max(bars[z] - pow(de, 2), bars[m_y]);
151             }
152         }
153     } else if (monstercat > 0) {
154         for (z = 0; z < number_of_bars; z++) {
155             // if (bars[z] < 1)bars[z] = 1;
156             for (m_y = z - 1; m_y >= 0; m_y--) {
157                 de = z - m_y;
158                 bars[m_y] = max(bars[z] / pow(monstercat, de), bars[m_y]);
159             }
160             for (m_y = z + 1; m_y < number_of_bars; m_y++) {
161                 de = m_y - z;
162                 bars[m_y] = max(bars[z] / pow(monstercat, de), bars[m_y]);
163             }
164         }
165     }
166 
167     return bars;
168 }
169 
170 // general: entry point
main(int argc,char ** argv)171 int main(int argc, char **argv) {
172 
173     // general: console title
174     printf("%c]0;%s%c", '\033', PACKAGE, '\007');
175 
176     // general: handle command-line arguments
177     char configPath[PATH_MAX];
178     configPath[0] = '\0';
179 
180     // general: handle Ctrl+C
181     struct sigaction action;
182     memset(&action, 0, sizeof(action));
183     action.sa_handler = &sig_handler;
184     sigaction(SIGINT, &action, NULL);
185     sigaction(SIGTERM, &action, NULL);
186     sigaction(SIGUSR1, &action, NULL);
187     sigaction(SIGUSR2, &action, NULL);
188 
189     char *usage = "\n\
190 Usage : " PACKAGE " [options]\n\
191 Visualize audio input in terminal. \n\
192 \n\
193 Options:\n\
194 	-p          path to config file\n\
195 	-v          print version\n\
196 \n\
197 Keys:\n\
198         Up        Increase sensitivity\n\
199         Down      Decrease sensitivity\n\
200         Left      Decrease number of bars\n\
201         Right     Increase number of bars\n\
202         r         Reload config\n\
203         c         Reload colors only\n\
204         f         Cycle foreground color\n\
205         b         Cycle background color\n\
206         q         Quit\n\
207 \n\
208 as of 0.4.0 all options are specified in config file, see in '/home/username/.config/cava/' \n";
209 
210     int c;
211     while ((c = getopt(argc, argv, "p:vh")) != -1) {
212         switch (c) {
213         case 'p': // argument: fifo path
214             snprintf(configPath, sizeof(configPath), "%s", optarg);
215             break;
216         case 'h': // argument: print usage
217             printf("%s", usage);
218             return 1;
219         case '?': // argument: print usage
220             printf("%s", usage);
221             return 1;
222         case 'v': // argument: print version
223             printf(PACKAGE " " VERSION "\n");
224             return 0;
225         default: // argument: no arguments; exit
226             abort();
227         }
228     }
229 
230     // general: main loop
231     while (1) {
232 
233         debug("loading config\n");
234         // config: load
235         struct error_s error;
236         error.length = 0;
237         if (!load_config(configPath, &p, 0, &error)) {
238             fprintf(stderr, "Error loading config. %s", error.message);
239             exit(EXIT_FAILURE);
240         }
241 
242         bool first = true;
243         int inAtty;
244 
245         output_mode = p.output;
246 
247         if (output_mode != OUTPUT_RAW) {
248             // Check if we're running in a tty
249             inAtty = 0;
250             if (strncmp(ttyname(0), "/dev/tty", 8) == 0 || strcmp(ttyname(0), "/dev/console") == 0)
251                 inAtty = 1;
252 
253             // in macos vitual terminals are called ttys(xyz) and there are no ttys
254             if (strncmp(ttyname(0), "/dev/ttys", 9) == 0)
255                 inAtty = 0;
256             if (inAtty) {
257                 // checking if cava psf font is installed in FONTDIR
258                 FILE *font_file;
259                 font_file = fopen(FONTDIR "/cava.fnt", "r");
260                 if (font_file) {
261                     fclose(font_file);
262 		     system("/usr/sbin/vidcontrol -f " FONTDIR "/cava.fnt > /dev/null 2>&1");
263                 } else {
264                     // if not it might still be available, we dont know, must try
265                     system("setfont cava.psf  >/dev/null 2>&1");
266                 }
267                 system("setterm -blank 0");
268             }
269 
270             // We use unicode block characters to draw the bars and
271             // the locale var LANG must be set to use unicode chars.
272             // For some reason this var can't be retrieved with
273             // setlocale(LANG, NULL), so we get it with getenv.
274             // Also we can't set it with setlocale(LANG "") so we
275             // must set LC_ALL instead.
276             // Attempting to set to en_US if not set, if that lang
277             // is not installed and LANG is not set there will be
278             // no output, for more info see #109 #344
279             if (!getenv("LANG"))
280                 setlocale(LC_ALL, "en_US.utf8");
281             else
282                 setlocale(LC_ALL, "");
283         }
284 
285         // input: init
286         int *bars_left, *bars_right;
287         double *temp_l, *temp_r;
288 
289         int bass_cut_off = 150;
290         int treble_cut_off = 2500;
291 
292         struct audio_data audio;
293         memset(&audio, 0, sizeof(audio));
294 
295         audio.source = malloc(1 + strlen(p.audio_source));
296         strcpy(audio.source, p.audio_source);
297 
298         audio.format = -1;
299         audio.rate = 0;
300         audio.FFTbassbufferSize = 4096;
301         audio.FFTmidbufferSize = 2048;
302         audio.FFTtreblebufferSize = 1024;
303         audio.terminate = 0;
304         if (p.stereo)
305             audio.channels = 2;
306         if (!p.stereo)
307             audio.channels = 1;
308         audio.average = false;
309         audio.left = false;
310         audio.right = false;
311         if (strcmp(p.mono_option, "average") == 0)
312             audio.average = true;
313         if (strcmp(p.mono_option, "left") == 0)
314             audio.left = true;
315         if (strcmp(p.mono_option, "right") == 0)
316             audio.right = true;
317         audio.bass_index = 0;
318         audio.mid_index = 0;
319         audio.treble_index = 0;
320         audio.bass_multiplier = (double *)malloc(audio.FFTbassbufferSize * sizeof(double));
321         audio.mid_multiplier = (double *)malloc(audio.FFTmidbufferSize * sizeof(double));
322         audio.treble_multiplier = (double *)malloc(audio.FFTtreblebufferSize * sizeof(double));
323 
324         temp_l = (double *)malloc((audio.FFTbassbufferSize / 2 + 1) * sizeof(double));
325         temp_r = (double *)malloc((audio.FFTbassbufferSize / 2 + 1) * sizeof(double));
326 
327         bars_left = (int *)malloc(256 * sizeof(int));
328         bars_right = (int *)malloc(256 * sizeof(int));
329 
330         for (int i = 0; i < audio.FFTbassbufferSize; i++) {
331             audio.bass_multiplier[i] =
332                 0.5 * (1 - cos(2 * M_PI * i / (audio.FFTbassbufferSize - 1)));
333         }
334         for (int i = 0; i < audio.FFTmidbufferSize; i++) {
335             audio.mid_multiplier[i] = 0.5 * (1 - cos(2 * M_PI * i / (audio.FFTmidbufferSize - 1)));
336         }
337         for (int i = 0; i < audio.FFTtreblebufferSize; i++) {
338             audio.treble_multiplier[i] =
339                 0.5 * (1 - cos(2 * M_PI * i / (audio.FFTtreblebufferSize - 1)));
340         }
341         // BASS
342         // audio.FFTbassbufferSize =  audio.rate / 20; // audio.FFTbassbufferSize;
343 
344         audio.in_bass_r = fftw_alloc_real(audio.FFTbassbufferSize);
345         audio.in_bass_l = fftw_alloc_real(audio.FFTbassbufferSize);
346         audio.in_bass_r_raw = fftw_alloc_real(audio.FFTbassbufferSize);
347         audio.in_bass_l_raw = fftw_alloc_real(audio.FFTbassbufferSize);
348 
349         out_bass_l = fftw_alloc_complex(audio.FFTbassbufferSize / 2 + 1);
350         out_bass_r = fftw_alloc_complex(audio.FFTbassbufferSize / 2 + 1);
351         memset(out_bass_l, 0, (audio.FFTbassbufferSize / 2 + 1) * sizeof(fftw_complex));
352         memset(out_bass_r, 0, (audio.FFTbassbufferSize / 2 + 1) * sizeof(fftw_complex));
353 
354         p_bass_l = fftw_plan_dft_r2c_1d(audio.FFTbassbufferSize, audio.in_bass_l, out_bass_l,
355                                         FFTW_MEASURE);
356         p_bass_r = fftw_plan_dft_r2c_1d(audio.FFTbassbufferSize, audio.in_bass_r, out_bass_r,
357                                         FFTW_MEASURE);
358 
359         // MID
360         // audio.FFTmidbufferSize =  audio.rate / bass_cut_off; // audio.FFTbassbufferSize;
361         audio.in_mid_r = fftw_alloc_real(audio.FFTmidbufferSize);
362         audio.in_mid_l = fftw_alloc_real(audio.FFTmidbufferSize);
363         audio.in_mid_r_raw = fftw_alloc_real(audio.FFTmidbufferSize);
364         audio.in_mid_l_raw = fftw_alloc_real(audio.FFTmidbufferSize);
365 
366         out_mid_l = fftw_alloc_complex(audio.FFTmidbufferSize / 2 + 1);
367         out_mid_r = fftw_alloc_complex(audio.FFTmidbufferSize / 2 + 1);
368         memset(out_mid_l, 0, (audio.FFTmidbufferSize / 2 + 1) * sizeof(fftw_complex));
369         memset(out_mid_r, 0, (audio.FFTmidbufferSize / 2 + 1) * sizeof(fftw_complex));
370 
371         p_mid_l =
372             fftw_plan_dft_r2c_1d(audio.FFTmidbufferSize, audio.in_mid_l, out_mid_l, FFTW_MEASURE);
373         p_mid_r =
374             fftw_plan_dft_r2c_1d(audio.FFTmidbufferSize, audio.in_mid_r, out_mid_r, FFTW_MEASURE);
375 
376         // TRIEBLE
377         // audio.FFTtreblebufferSize =  audio.rate / treble_cut_off; // audio.FFTbassbufferSize;
378         audio.in_treble_r = fftw_alloc_real(audio.FFTtreblebufferSize);
379         audio.in_treble_l = fftw_alloc_real(audio.FFTtreblebufferSize);
380         audio.in_treble_r_raw = fftw_alloc_real(audio.FFTtreblebufferSize);
381         audio.in_treble_l_raw = fftw_alloc_real(audio.FFTtreblebufferSize);
382 
383         out_treble_l = fftw_alloc_complex(audio.FFTtreblebufferSize / 2 + 1);
384         out_treble_r = fftw_alloc_complex(audio.FFTtreblebufferSize / 2 + 1);
385         memset(out_treble_l, 0, (audio.FFTtreblebufferSize / 2 + 1) * sizeof(fftw_complex));
386         memset(out_treble_r, 0, (audio.FFTtreblebufferSize / 2 + 1) * sizeof(fftw_complex));
387 
388         p_treble_l = fftw_plan_dft_r2c_1d(audio.FFTtreblebufferSize, audio.in_treble_l,
389                                           out_treble_l, FFTW_MEASURE);
390         p_treble_r = fftw_plan_dft_r2c_1d(audio.FFTtreblebufferSize, audio.in_treble_r,
391                                           out_treble_r, FFTW_MEASURE);
392 
393         debug("got buffer size: %d, %d, %d", audio.FFTbassbufferSize, audio.FFTmidbufferSize,
394               audio.FFTtreblebufferSize);
395 
396         reset_output_buffers(&audio);
397 
398         debug("starting audio thread\n");
399 
400         pthread_t p_thread;
401         int timeout_counter = 0;
402 
403         struct timespec timeout_timer = {.tv_sec = 0, .tv_nsec = 1000000};
404         int thr_id GCC_UNUSED;
405 
406         switch (p.input) {
407 #ifdef ALSA
408         case INPUT_ALSA:
409             // input_alsa: wait for the input to be ready
410             if (is_loop_device_for_sure(audio.source)) {
411                 if (directory_exists("/sys/")) {
412                     if (!directory_exists("/sys/module/snd_aloop/")) {
413                         cleanup();
414                         fprintf(stderr,
415                                 "Linux kernel module \"snd_aloop\" does not seem to  be loaded.\n"
416                                 "Maybe run \"sudo modprobe snd_aloop\".\n");
417                         exit(EXIT_FAILURE);
418                     }
419                 }
420             }
421 
422             thr_id = pthread_create(&p_thread, NULL, input_alsa,
423                                     (void *)&audio); // starting alsamusic listener
424 
425             timeout_counter = 0;
426 
427             while (audio.format == -1 || audio.rate == 0) {
428                 nanosleep(&timeout_timer, NULL);
429                 timeout_counter++;
430                 if (timeout_counter > 2000) {
431                     cleanup();
432                     fprintf(stderr, "could not get rate and/or format, problems with audio thread? "
433                                     "quiting...\n");
434                     exit(EXIT_FAILURE);
435                 }
436             }
437             debug("got format: %d and rate %d\n", audio.format, audio.rate);
438             break;
439 #endif
440         case INPUT_FIFO:
441             // starting fifomusic listener
442             thr_id = pthread_create(&p_thread, NULL, input_fifo, (void *)&audio);
443             audio.rate = p.fifoSample;
444             audio.format = p.fifoSampleBits;
445             break;
446 #ifdef PULSE
447         case INPUT_PULSE:
448             if (strcmp(audio.source, "auto") == 0) {
449                 getPulseDefaultSink((void *)&audio);
450             }
451             // starting pulsemusic listener
452             thr_id = pthread_create(&p_thread, NULL, input_pulse, (void *)&audio);
453             audio.rate = 44100;
454             break;
455 #endif
456 #ifdef SNDIO
457         case INPUT_SNDIO:
458             thr_id = pthread_create(&p_thread, NULL, input_sndio, (void *)&audio);
459             audio.rate = 44100;
460             break;
461 #endif
462         case INPUT_SHMEM:
463             thr_id = pthread_create(&p_thread, NULL, input_shmem, (void *)&audio);
464 
465             timeout_counter = 0;
466             while (audio.rate == 0) {
467                 nanosleep(&timeout_timer, NULL);
468                 timeout_counter++;
469                 if (timeout_counter > 2000) {
470                     cleanup();
471                     fprintf(stderr, "could not get rate and/or format, problems with audio thread? "
472                                     "quiting...\n");
473                     exit(EXIT_FAILURE);
474                 }
475             }
476             debug("got format: %d and rate %d\n", audio.format, audio.rate);
477             // audio.rate = 44100;
478             break;
479 #ifdef PORTAUDIO
480         case INPUT_PORTAUDIO:
481             thr_id = pthread_create(&p_thread, NULL, input_portaudio, (void *)&audio);
482             audio.rate = 44100;
483             break;
484 #endif
485         default:
486             exit(EXIT_FAILURE); // Can't happen.
487         }
488 
489         if (p.upper_cut_off > audio.rate / 2) {
490             cleanup();
491             fprintf(stderr, "higher cuttoff frequency can't be higher than sample rate / 2");
492             exit(EXIT_FAILURE);
493         }
494 
495         int bars[256];
496         int bars_mem[256];
497         int bars_last[256];
498         int previous_frame[256];
499         int fall[256];
500         float bars_peak[256];
501 
502         int height, lines, width, remainder, fp;
503 
504         bool reloadConf = false;
505         while (!reloadConf) { // jumping back to this loop means that you resized the screen
506             for (int n = 0; n < 256; n++) {
507                 bars_last[n] = 0;
508                 previous_frame[n] = 0;
509                 fall[n] = 0;
510                 bars_peak[n] = 0;
511                 bars_mem[n] = 0;
512                 bars[n] = 0;
513             }
514 
515             // frequencies on x axis require a bar width of four or more
516             if (p.xaxis == FREQUENCY && p.bar_width < 4)
517                 p.bar_width = 4;
518 
519             switch (output_mode) {
520 #ifdef NCURSES
521             // output: start ncurses mode
522             case OUTPUT_NCURSES:
523                 init_terminal_ncurses(p.color, p.bcolor, p.col, p.bgcol, p.gradient,
524                                       p.gradient_count, p.gradient_colors, &width, &lines);
525                 if (p.xaxis != NONE)
526                     lines--;
527                 // we have 8 times as much height due to using 1/8 block characters
528                 height = lines * 8;
529                 break;
530 #endif
531             case OUTPUT_RAW:
532                 if (strcmp(p.raw_target, "/dev/stdout") != 0) {
533                     int fptest;
534                     // checking if file exists
535                     if (access(p.raw_target, F_OK) != -1) {
536                         // testopening in case it's a fifo
537                         fptest = open(p.raw_target, O_RDONLY | O_NONBLOCK, 0644);
538 
539                         if (fptest == -1) {
540                             printf("could not open file %s for writing\n", p.raw_target);
541                             exit(1);
542                         }
543                     } else {
544                         printf("creating fifo %s\n", p.raw_target);
545                         if (mkfifo(p.raw_target, 0664) == -1) {
546                             printf("could not create fifo %s\n", p.raw_target);
547                             exit(1);
548                         }
549                         // fifo needs to be open for reading in order to write to it
550                         fptest = open(p.raw_target, O_RDONLY | O_NONBLOCK, 0644);
551                     }
552                 }
553                 fp = open(p.raw_target, O_WRONLY | O_NONBLOCK | O_CREAT, 0644);
554                 if (fp == -1) {
555                     printf("could not open file %s for writing\n", p.raw_target);
556                     exit(1);
557                 }
558                 printf("open file %s for writing raw output\n", p.raw_target);
559 
560                 // width must be hardcoded for raw output.
561                 width = 256;
562 
563                 if (strcmp(p.data_format, "binary") == 0) {
564                     height = pow(2, p.bit_format) - 1;
565                 } else {
566                     height = p.ascii_range;
567                 }
568                 break;
569 
570             default:
571                 exit(EXIT_FAILURE); // Can't happen.
572             }
573 
574             // handle for user setting too many bars
575             if (p.fixedbars) {
576                 p.autobars = 0;
577                 if (p.fixedbars * p.bar_width + p.fixedbars * p.bar_spacing - p.bar_spacing > width)
578                     p.autobars = 1;
579             }
580 
581             // getting numbers of bars
582             int number_of_bars = p.fixedbars;
583 
584             if (p.autobars == 1)
585                 number_of_bars = (width + p.bar_spacing) / (p.bar_width + p.bar_spacing);
586 
587             if (number_of_bars < 1)
588                 number_of_bars = 1; // must have at least 1 bars
589             if (number_of_bars > 256)
590                 number_of_bars = 256; // cant have more than 256 bars
591 
592             if (p.stereo) { // stereo must have even numbers of bars
593                 if (number_of_bars % 2 != 0)
594                     number_of_bars--;
595             }
596 
597             // checks if there is stil extra room, will use this to center
598             remainder = (width - number_of_bars * p.bar_width - number_of_bars * p.bar_spacing +
599                          p.bar_spacing) /
600                         2;
601             if (remainder < 0)
602                 remainder = 0;
603 
604             // process [smoothing]: calculate gravity
605             float g = p.gravity * ((float)height / 2160) * pow((60 / (float)p.framerate), 2.5);
606 
607             // calculate integral value, must be reduced with height
608             double integral = p.integral;
609             if (height > 320)
610                 integral = p.integral * 1 / sqrt((log10((float)height / 10)));
611 
612 #ifndef NDEBUG
613             debug("height: %d width: %d bars:%d bar width: %d remainder: %d\n", height, width,
614                   number_of_bars, p.bar_width, remainder);
615 #endif
616 
617             // process: calculate cutoff frequencies and eq
618             if (p.stereo)
619                 number_of_bars =
620                     number_of_bars / 2; // in stereo only half number of number_of_bars per channel
621                                         // for cutoff frequencies and eq calculation
622 
623             double userEQ_keys_to_bars_ratio;
624 
625             if (p.userEQ_enabled && (number_of_bars > 0)) {
626                 userEQ_keys_to_bars_ratio =
627                     (double)(((double)p.userEQ_keys) / ((double)number_of_bars));
628             }
629 
630             // calculate frequency constant (used to distribute bars across the frequency band)
631             double frequency_constant = log10((float)p.lower_cut_off / (float)p.upper_cut_off) /
632                                         (1 / ((float)number_of_bars + 1) - 1);
633 
634             float cut_off_frequency[256];
635             float upper_cut_off_frequency[256];
636             float relative_cut_off[256];
637             double center_frequencies[256];
638             int FFTbuffer_lower_cut_off[256];
639             int FFTbuffer_upper_cut_off[256];
640             double eq[256];
641 
642             int bass_cut_off_bar = -1;
643             int treble_cut_off_bar = -1;
644             bool first_bar = true;
645             int first_treble_bar = 0;
646             int bar_buffer[number_of_bars + 1];
647 
648             for (int n = 0; n < number_of_bars + 1; n++) {
649                 double bar_distribution_coefficient = frequency_constant * (-1);
650                 bar_distribution_coefficient +=
651                     ((float)n + 1) / ((float)number_of_bars + 1) * frequency_constant;
652                 cut_off_frequency[n] = p.upper_cut_off * pow(10, bar_distribution_coefficient);
653 
654                 if (n > 0) {
655                     if (cut_off_frequency[n - 1] >= cut_off_frequency[n] &&
656                         cut_off_frequency[n - 1] > bass_cut_off)
657                         cut_off_frequency[n] =
658                             cut_off_frequency[n - 1] +
659                             (cut_off_frequency[n - 1] - cut_off_frequency[n - 2]);
660                 }
661 
662                 relative_cut_off[n] = cut_off_frequency[n] / (audio.rate / 2);
663                 // remember nyquist!, per my calculations this should be rate/2
664                 // and nyquist freq in M/2 but testing shows it is not...
665                 // or maybe the nq freq is in M/4
666 
667                 eq[n] = pow(cut_off_frequency[n], 1);
668 
669                 // the numbers that come out of the FFT are verry high
670                 // the EQ is used to "normalize" them by dividing with this verry huge number
671                 eq[n] *= (float)height / pow(2, 28);
672 
673                 if (p.userEQ_enabled)
674                     eq[n] *= p.userEQ[(int)floor(((double)n) * userEQ_keys_to_bars_ratio)];
675 
676                 eq[n] /= log2(audio.FFTbassbufferSize);
677 
678                 if (cut_off_frequency[n] < bass_cut_off) {
679                     // BASS
680                     bar_buffer[n] = 1;
681                     FFTbuffer_lower_cut_off[n] =
682                         relative_cut_off[n] * (audio.FFTbassbufferSize / 2);
683                     bass_cut_off_bar++;
684                     treble_cut_off_bar++;
685                     if (bass_cut_off_bar > 0)
686                         first_bar = false;
687 
688                     eq[n] *= log2(audio.FFTbassbufferSize);
689                 } else if (cut_off_frequency[n] > bass_cut_off &&
690                            cut_off_frequency[n] < treble_cut_off) {
691                     // MID
692                     bar_buffer[n] = 2;
693                     FFTbuffer_lower_cut_off[n] = relative_cut_off[n] * (audio.FFTmidbufferSize / 2);
694                     treble_cut_off_bar++;
695                     if ((treble_cut_off_bar - bass_cut_off_bar) == 1) {
696                         first_bar = true;
697                         FFTbuffer_upper_cut_off[n - 1] =
698                             relative_cut_off[n] * (audio.FFTbassbufferSize / 2);
699                     } else {
700                         first_bar = false;
701                     }
702 
703                     eq[n] *= log2(audio.FFTmidbufferSize);
704                 } else {
705                     // TREBLE
706                     bar_buffer[n] = 3;
707                     FFTbuffer_lower_cut_off[n] =
708                         relative_cut_off[n] * (audio.FFTtreblebufferSize / 2);
709                     first_treble_bar++;
710                     if (first_treble_bar == 1) {
711                         first_bar = true;
712                         FFTbuffer_upper_cut_off[n - 1] =
713                             relative_cut_off[n] * (audio.FFTmidbufferSize / 2);
714                     } else {
715                         first_bar = false;
716                     }
717 
718                     eq[n] *= log2(audio.FFTtreblebufferSize);
719                 }
720 
721                 if (n > 0) {
722                     if (!first_bar) {
723                         FFTbuffer_upper_cut_off[n - 1] = FFTbuffer_lower_cut_off[n] - 1;
724 
725                         // pushing the spectrum up if the exponential function gets "clumped" in the
726                         // bass and caluclating new cut off frequencies
727                         if (FFTbuffer_lower_cut_off[n] <= FFTbuffer_lower_cut_off[n - 1]) {
728 
729                             FFTbuffer_lower_cut_off[n] = FFTbuffer_lower_cut_off[n - 1] + 1;
730                             FFTbuffer_upper_cut_off[n - 1] = FFTbuffer_lower_cut_off[n] - 1;
731 
732                             if (bar_buffer[n] == 1)
733                                 relative_cut_off[n] = (float)(FFTbuffer_lower_cut_off[n]) /
734                                                       ((float)audio.FFTbassbufferSize / 2);
735                             else if (bar_buffer[n] == 2)
736                                 relative_cut_off[n] = (float)(FFTbuffer_lower_cut_off[n]) /
737                                                       ((float)audio.FFTmidbufferSize / 2);
738                             else if (bar_buffer[n] == 3)
739                                 relative_cut_off[n] = (float)(FFTbuffer_lower_cut_off[n]) /
740                                                       ((float)audio.FFTtreblebufferSize / 2);
741 
742                             cut_off_frequency[n] = relative_cut_off[n] * ((float)audio.rate / 2);
743                         }
744                     } else {
745                         if (FFTbuffer_upper_cut_off[n - 1] <= FFTbuffer_lower_cut_off[n - 1])
746                             FFTbuffer_upper_cut_off[n - 1] = FFTbuffer_lower_cut_off[n - 1] + 1;
747                     }
748                     upper_cut_off_frequency[n - 1] =
749                         cut_off_frequency[n]; // high_relative_cut_off * ((float)audio.rate / 2);
750                     center_frequencies[n - 1] =
751                         pow((cut_off_frequency[n - 1] * upper_cut_off_frequency[n - 1]), 0.5);
752                 }
753 
754 #ifndef NDEBUG
755                 initscr();
756                 curs_set(0);
757                 timeout(0);
758                 if (n != 0) {
759                     mvprintw(n, 0, "%d: %f -> %f (%d -> %d) bass: %d, treble:%d \n", n,
760                              cut_off_frequency[n - 1], cut_off_frequency[n],
761                              FFTbuffer_lower_cut_off[n - 1], FFTbuffer_upper_cut_off[n - 1],
762                              bass_cut_off_bar, treble_cut_off_bar);
763                 }
764 #endif
765             }
766 
767             if (p.stereo)
768                 number_of_bars = number_of_bars * 2;
769 
770             // process: calculate x axis values
771             int x_axis_info = 0;
772 
773             if (p.xaxis != NONE) {
774                 x_axis_info = 1;
775                 double center_frequency;
776                 for (int n = 0; n < number_of_bars; n++) {
777                     if (p.stereo) {
778                         if (n < number_of_bars / 2)
779                             center_frequency = center_frequencies[number_of_bars / 2 - 1 - n];
780                         else
781                             center_frequency = center_frequencies[n - number_of_bars / 2];
782                     } else {
783                         center_frequency = center_frequencies[n];
784                     }
785 
786                     float freq_kilohz = center_frequency / 1000;
787                     int freq_floor = center_frequency;
788 
789                     if (output_mode == OUTPUT_NCURSES) {
790 #ifdef NCURSES
791                         if (center_frequency < 1000)
792                             mvprintw(lines, n * (p.bar_width + p.bar_spacing) + remainder, "%-4d",
793                                      freq_floor);
794                         else if (center_frequency > 1000 && center_frequency < 10000)
795                             mvprintw(lines, n * (p.bar_width + p.bar_spacing) + remainder, "%.2f",
796                                      freq_kilohz);
797                         else
798                             mvprintw(lines, n * (p.bar_width + p.bar_spacing) + remainder, "%.1f",
799                                      freq_kilohz);
800 #endif
801                     }
802                 }
803                 printf("\r\033[%dA", lines + 1);
804             }
805 
806             bool resizeTerminal = false;
807 
808             struct timespec framerate_timer = {.tv_sec = 0, .tv_nsec = 0};
809             if (p.framerate <= 1) {
810                 framerate_timer.tv_sec = 1 / (float)p.framerate;
811             } else {
812                 framerate_timer.tv_sec = 0;
813                 framerate_timer.tv_nsec = (1 / (float)p.framerate) * 1e9;
814             }
815 
816             int sleep_counter = 0;
817             bool silence = false;
818             char ch = '\0';
819 
820 #ifndef NDEBUG
821             int maxvalue = 0;
822             int minvalue = 0;
823 #endif
824 
825             struct timespec sleep_mode_timer = {.tv_sec = 1, .tv_nsec = 0};
826 
827             while (!resizeTerminal) {
828 
829 // general: keyboard controls
830 #ifdef NCURSES
831                 if (output_mode == OUTPUT_NCURSES)
832                     ch = getch();
833 #endif
834                 /*
835                 // disabled key controls in non-curses mode, caused garbage on screen
836                 if (output_mode == OUTPUT_NONCURSES)
837                     ch = fgetc(stdin);
838                 */
839 
840                 switch (ch) {
841                 case 65: // key up
842                     p.sens = p.sens * 1.05;
843                     break;
844                 case 66: // key down
845                     p.sens = p.sens * 0.95;
846                     break;
847                 case 68: // key right
848                     p.bar_width++;
849                     resizeTerminal = true;
850                     break;
851                 case 67: // key left
852                     if (p.bar_width > 1)
853                         p.bar_width--;
854                     resizeTerminal = true;
855                     break;
856                 case 'r': // reload config
857                     should_reload = 1;
858                     break;
859                 case 'c': // reload colors
860                     reload_colors = 1;
861                     break;
862                 case 'f': // change forground color
863                     if (p.col < 7)
864                         p.col++;
865                     else
866                         p.col = 0;
867                     resizeTerminal = true;
868                     break;
869                 case 'b': // change backround color
870                     if (p.bgcol < 7)
871                         p.bgcol++;
872                     else
873                         p.bgcol = 0;
874                     resizeTerminal = true;
875                     break;
876 
877                 case 'q':
878                     should_reload = 1;
879                     should_quit = 1;
880                 }
881 
882                 if (should_reload) {
883 
884                     reloadConf = true;
885                     resizeTerminal = true;
886                     should_reload = 0;
887                 }
888 
889                 if (reload_colors) {
890                     struct error_s error;
891                     error.length = 0;
892                     if (!load_config(configPath, (void *)&p, 1, &error)) {
893                         cleanup();
894                         fprintf(stderr, "Error loading config. %s", error.message);
895                         exit(EXIT_FAILURE);
896                     }
897                     resizeTerminal = true;
898                     reload_colors = 0;
899                 }
900 
901 #ifndef NDEBUG
902                 // clear();
903                 refresh();
904 #endif
905 
906                 // process: check if input is present
907                 silence = true;
908 
909                 for (int n = 0; n < audio.FFTbassbufferSize; n++) {
910                     if (audio.in_bass_l[n] || audio.in_bass_r[n]) {
911                         silence = false;
912                         break;
913                     }
914                 }
915 
916                 if (p.sleep_timer) {
917                     if (silence && sleep_counter <= p.framerate * p.sleep_timer)
918                         sleep_counter++;
919                     else if (!silence)
920                         sleep_counter = 0;
921 
922                     if (sleep_counter > p.framerate * p.sleep_timer) {
923 #ifndef NDEBUG
924                         printw("no sound detected for 30 sec, going to sleep mode\n");
925 #endif
926                         nanosleep(&sleep_mode_timer, NULL);
927                         continue;
928                     }
929                 }
930 
931                 // process: execute FFT and sort frequency bands
932                 pthread_mutex_lock(&lock);
933                 fftw_execute(p_bass_l);
934                 fftw_execute(p_mid_l);
935                 fftw_execute(p_treble_l);
936                 if (p.stereo) {
937                     fftw_execute(p_bass_r);
938                     fftw_execute(p_mid_r);
939                     fftw_execute(p_treble_r);
940                     number_of_bars /= 2;
941                 }
942                 pthread_mutex_unlock(&lock);
943 
944                 // process: separate frequency bands
945                 for (int n = 0; n < number_of_bars; n++) {
946 
947                     temp_l[n] = 0;
948                     if (p.stereo)
949                         temp_r[n] = 0;
950 
951                     // process: add upp FFT values within bands
952                     for (int i = FFTbuffer_lower_cut_off[n]; i <= FFTbuffer_upper_cut_off[n]; i++) {
953                         if (n <= bass_cut_off_bar) {
954 
955                             temp_l[n] += hypot(out_bass_l[i][0], out_bass_l[i][1]);
956 
957                             if (p.stereo)
958                                 temp_r[n] += hypot(out_bass_r[i][0], out_bass_r[i][1]);
959 
960                         } else if (n > bass_cut_off_bar && n <= treble_cut_off_bar) {
961 
962                             temp_l[n] += hypot(out_mid_l[i][0], out_mid_l[i][1]);
963 
964                             if (p.stereo)
965                                 temp_r[n] += hypot(out_mid_r[i][0], out_mid_r[i][1]);
966 
967                         } else if (n > treble_cut_off_bar) {
968 
969                             temp_l[n] += hypot(out_treble_l[i][0], out_treble_l[i][1]);
970 
971                             if (p.stereo)
972                                 temp_r[n] += hypot(out_treble_r[i][0], out_treble_r[i][1]);
973                         }
974                     }
975 
976                     // getting average multiply with sens and eq
977                     temp_l[n] /= FFTbuffer_upper_cut_off[n] - FFTbuffer_lower_cut_off[n] + 1;
978                     temp_l[n] *= p.sens * eq[n];
979 
980                     if (temp_l[n] <= p.ignore)
981                         temp_l[n] = 0;
982 
983                     bars_left[n] = temp_l[n];
984 
985                     if (p.stereo) {
986                         temp_r[n] /= FFTbuffer_upper_cut_off[n] - FFTbuffer_lower_cut_off[n] + 1;
987                         temp_r[n] *= p.sens * eq[n];
988 
989                         if (temp_r[n] <= p.ignore)
990                             temp_r[n] = 0;
991 
992                         bars_right[n] = temp_r[n];
993                     }
994                 }
995                 if (p.stereo)
996                     number_of_bars *= 2;
997                 // process [filter]
998 
999                 if (p.monstercat) {
1000                     if (p.stereo) {
1001                         bars_left =
1002                             monstercat_filter(bars_left, number_of_bars / 2, p.waves, p.monstercat);
1003                         bars_right = monstercat_filter(bars_right, number_of_bars / 2, p.waves,
1004                                                        p.monstercat);
1005                     } else {
1006                         bars_left =
1007                             monstercat_filter(bars_left, number_of_bars, p.waves, p.monstercat);
1008                     }
1009                 }
1010 
1011                 // processing signal
1012 
1013                 bool senselow = true;
1014 
1015                 for (int n = 0; n < number_of_bars; n++) {
1016                     // mirroring stereo channels
1017                     if (p.stereo) {
1018                         if (n < number_of_bars / 2) {
1019                             bars[n] = bars_left[number_of_bars / 2 - n - 1];
1020                         } else {
1021                             bars[n] = bars_right[n - number_of_bars / 2];
1022                         }
1023 
1024                     } else {
1025                         bars[n] = bars_left[n];
1026                     }
1027 
1028                     // process [smoothing]: falloff
1029                     if (g > 0) {
1030                         if (bars[n] < bars_last[n]) {
1031                             bars[n] = bars_peak[n] - (g * fall[n] * fall[n]);
1032                             if (bars[n] < 0)
1033                                 bars[n] = 0;
1034                             fall[n]++;
1035                         } else {
1036                             bars_peak[n] = bars[n];
1037                             fall[n] = 0;
1038                         }
1039 
1040                         bars_last[n] = bars[n];
1041                     }
1042 
1043                     // process [smoothing]: integral
1044                     if (p.integral > 0) {
1045                         bars[n] = bars_mem[n] * integral + bars[n];
1046                         bars_mem[n] = bars[n];
1047 
1048                         int diff = height - bars[n];
1049                         if (diff < 0)
1050                             diff = 0;
1051                         double div = 1 / (diff + 1);
1052                         // bars[n] = bars[n] - pow(div, 10) * (height + 1);
1053                         bars_mem[n] = bars_mem[n] * (1 - div / 20);
1054                     }
1055 #ifndef NDEBUG
1056                     mvprintw(n, 0, "%d: f:%f->%f (%d->%d), eq:\
1057 						%15e, peak:%d \n",
1058                              n, cut_off_frequency[n], cut_off_frequency[n + 1],
1059                              FFTbuffer_lower_cut_off[n], FFTbuffer_upper_cut_off[n], eq[n],
1060                              bars[n]);
1061 
1062                     if (bars[n] < minvalue) {
1063                         minvalue = bars[n];
1064                         debug("min value: %d\n", minvalue); // checking maxvalue 10000
1065                     }
1066                     if (bars[n] > maxvalue) {
1067                         maxvalue = bars[n];
1068                     }
1069                     if (bars[n] < 0) {
1070                         debug("negative bar value!! %d\n", bars[n]);
1071                         //    exit(EXIT_FAILURE); // Can't happen.
1072                     }
1073 
1074 #endif
1075 
1076                     // zero values causes divided by zero segfault (if not raw)
1077                     if (output_mode != OUTPUT_RAW && bars[n] < 1)
1078                         bars[n] = 1;
1079 
1080                     // automatic sense adjustment
1081                     if (p.autosens && !silence) {
1082                         if (bars[n] > height && senselow) {
1083                             p.sens = p.sens * 0.98;
1084                             senselow = false;
1085                             first = false;
1086                         }
1087                     }
1088                 }
1089 
1090                 if (p.autosens && !silence && senselow) {
1091                     p.sens = p.sens * 1.001;
1092                     if (first)
1093                         p.sens = p.sens * 1.1;
1094                 }
1095 
1096 #ifndef NDEBUG
1097                 mvprintw(number_of_bars + 1, 0, "sensitivity %.10e", p.sens);
1098                 mvprintw(number_of_bars + 2, 0, "min value: %d\n",
1099                          minvalue); // checking maxvalue 10000
1100                 mvprintw(number_of_bars + 3, 0, "max value: %d\n",
1101                          maxvalue); // checking maxvalue 10000
1102                 (void)x_axis_info;
1103 #endif
1104 
1105 // output: draw processed input
1106 #ifdef NDEBUG
1107                 int rc;
1108                 switch (output_mode) {
1109                 case OUTPUT_NCURSES:
1110 #ifdef NCURSES
1111                     rc = draw_terminal_ncurses(inAtty, lines, width, number_of_bars, p.bar_width,
1112                                                p.bar_spacing, remainder, bars, previous_frame,
1113                                                p.gradient, x_axis_info);
1114                     break;
1115 #endif
1116                 case OUTPUT_RAW:
1117                     rc = print_raw_out(number_of_bars, fp, p.is_bin, p.bit_format, p.ascii_range,
1118                                        p.bar_delim, p.frame_delim, bars);
1119                     break;
1120 
1121                 default:
1122                     exit(EXIT_FAILURE); // Can't happen.
1123                 }
1124 
1125                 // terminal has been resized breaking to recalibrating values
1126                 if (rc == -1)
1127                     resizeTerminal = true;
1128 
1129 #endif
1130 
1131                 memcpy(previous_frame, bars, 256 * sizeof(int));
1132 
1133                 // checking if audio thread has exited unexpectedly
1134                 if (audio.terminate == 1) {
1135                     cleanup();
1136                     fprintf(stderr, "Audio thread exited unexpectedly. %s\n", audio.error_message);
1137                     exit(EXIT_FAILURE);
1138                 }
1139 
1140                 nanosleep(&framerate_timer, NULL);
1141             } // resize terminal
1142 
1143         } // reloading config
1144 
1145         //**telling audio thread to terminate**//
1146         audio.terminate = 1;
1147         pthread_join(p_thread, NULL);
1148 
1149         if (p.userEQ_enabled)
1150             free(p.userEQ);
1151 
1152         free(audio.source);
1153 
1154         fftw_free(audio.in_bass_r);
1155         fftw_free(audio.in_bass_l);
1156         fftw_free(out_bass_r);
1157         fftw_free(out_bass_l);
1158         fftw_destroy_plan(p_bass_l);
1159         fftw_destroy_plan(p_bass_r);
1160 
1161         fftw_free(audio.in_mid_r);
1162         fftw_free(audio.in_mid_l);
1163         fftw_free(out_mid_r);
1164         fftw_free(out_mid_l);
1165         fftw_destroy_plan(p_mid_l);
1166         fftw_destroy_plan(p_mid_r);
1167 
1168         fftw_free(audio.in_treble_r);
1169         fftw_free(audio.in_treble_l);
1170         fftw_free(out_treble_r);
1171         fftw_free(out_treble_l);
1172         fftw_destroy_plan(p_treble_l);
1173         fftw_destroy_plan(p_treble_r);
1174 
1175         cleanup();
1176 
1177         if (should_quit)
1178             return EXIT_SUCCESS;
1179 
1180         // fclose(fp);
1181     }
1182 }
1183