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