nausea.c (17967B)
1 #include <err.h> 2 #include <complex.h> 3 #include <curses.h> 4 #include <fcntl.h> 5 #include <locale.h> 6 #include <math.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <stdint.h> 10 #include <stdlib.h> 11 #include <unistd.h> 12 #include <wchar.h> 13 14 #include <fftw3.h> 15 16 #define LEN(x) (sizeof (x) / sizeof *(x)) 17 #define MIN(x, y) ((x) < (y) ? (x) : (y)) 18 #define MAX(x, y) ((x) > (y) ? (x) : (y)) 19 #define WCSLEN(s) (LEN(s) - 2) 20 21 #include "config.h" 22 23 static unsigned msec = 1000 / 25; /* 25 fps */ 24 static unsigned nsamples = 44100 * 2; /* stereo */ 25 static unsigned dftlen = 8192; 26 static unsigned dftout = 8192 / 2 + 1; 27 static wchar_t chbar = CHBAR; 28 static wchar_t chpeak = CHPEAK; 29 static wchar_t chpoint = CHPOINT; 30 static wchar_t intensity[] = INTENSITY; 31 static char *fname = "/tmp/audio.fifo"; 32 static char *argv0; 33 static int colors; 34 static int peaks; 35 static int keep; 36 static int left; 37 static int bounce; 38 static int die; 39 static int freeze; 40 static int stereo; 41 static int debug; 42 43 struct frame { 44 int fd; 45 size_t width, width_old; 46 size_t height, height_old; 47 int *peak; 48 int *sav; 49 #define PK_HIDDEN -1 50 int16_t *buf; 51 float *res; 52 double *in; 53 size_t gotsamples; 54 complex *out; 55 fftw_plan plan; 56 }; 57 58 /* Supported visualizations: 59 * 1 -- spectrum 60 * 2 -- fountain 61 * 3 -- wave 62 * 4 -- boom 63 * 5 -- solid 64 * 6 -- spectro 65 */ 66 static void draw_spectrum(struct frame *fr); 67 static void draw_fountain(struct frame *fr); 68 static void draw_wave(struct frame *fr); 69 static void draw_boom(struct frame *fr); 70 static void draw_solid(struct frame *fr); 71 static void draw_spectro(struct frame *fr); 72 static struct visual { 73 void (* draw)(struct frame *fr); 74 int dft; /* needs the DFT */ 75 int color; /* supports colors */ 76 int stereo; /* supports stereo */ 77 } visuals[] = { 78 { draw_spectrum, 1, 1, 0 }, 79 { draw_fountain, 1, 1, 0 }, 80 { draw_wave, 0, 0, 0 }, 81 { draw_boom, 0, 1, 0 }, 82 { draw_solid, 0, 1, 1 }, 83 { draw_spectro, 1, 0, 0 }, 84 }; 85 static int vidx = 0; /* default visual index */ 86 87 /* We assume the screen is 100 pixels in the y direction. 88 * To follow the curses convention (0, 0) is in the top left 89 * corner of the screen. The `min' and `max' values correspond 90 * to percentages. To illustrate this the [0, 20) range gives 91 * the top 20% of the screen to the color red. These values 92 * are scaled automatically in the draw() routine to the actual 93 * size of the terminal window. */ 94 static struct color_range { 95 short pair; /* index in the color table */ 96 int min; /* min % */ 97 int max; /* max % */ 98 short fg; /* foreground color */ 99 short bg; /* background color */ 100 101 /* these are calculated internally, do not set */ 102 int scaled_min; 103 int scaled_max; 104 } color_ranges[] = { 105 { 1, 0, 20, COLOR_RED, -1, 0, 0 }, 106 { 2, 20, 60, COLOR_YELLOW, -1, 0, 0 }, 107 { 3, 60, 100, COLOR_GREEN, -1, 0, 0 } 108 }; 109 110 static void 111 clearall(struct frame *fr) 112 { 113 size_t i; 114 115 fr->gotsamples = 0; 116 117 for (i = 0; i < nsamples; i++) 118 fr->in[i] = 0.; 119 for (i = 0; i < dftout; i++) 120 fr->out[i] = 0. + 0. * I; 121 } 122 123 static void 124 init(struct frame *fr) 125 { 126 fr->fd = open(fname, O_RDONLY | O_NONBLOCK); 127 if (fr->fd == -1) 128 err(1, "open %s", fname); 129 130 fr->buf = malloc(nsamples * sizeof(int16_t)); 131 fr->in = malloc(nsamples * sizeof(double)); 132 133 /* these are used only by DFT visuals */ 134 fr->out = malloc(dftout * sizeof(complex)); 135 fr->res = malloc(dftout * sizeof(float)); 136 137 clearall(fr); 138 139 /* we expect single channel input, so half the samples */ 140 fr->plan = fftw_plan_dft_r2c_1d(dftlen, fr->in, fr->out, 141 FFTW_ESTIMATE); 142 } 143 144 static void 145 done(struct frame *fr) 146 { 147 fftw_destroy_plan(fr->plan); 148 free(fr->out); 149 free(fr->in); 150 151 free(fr->res); 152 free(fr->buf); 153 free(fr->peak); 154 free(fr->sav); 155 156 close(fr->fd); 157 } 158 159 static void 160 update(struct frame *fr) 161 { 162 ssize_t n; 163 164 n = read(fr->fd, fr->buf, nsamples * sizeof(int16_t)); 165 if (n == -1) { 166 clearall(fr); 167 return; 168 } 169 170 fr->gotsamples = n / sizeof(int16_t); 171 } 172 173 static void 174 stagestereo(struct frame *fr) 175 { 176 size_t i; 177 178 for (i = 0; i < nsamples; i++) { 179 fr->in[i] = 0.; 180 if (i < fr->gotsamples) 181 fr->in[i] = fr->buf[i]; 182 } 183 } 184 185 static void 186 stagemono(struct frame *fr) 187 { 188 size_t i; 189 190 /* we have half the samples after the merge */ 191 fr->gotsamples /= 2; 192 193 for (i = 0; i < nsamples; i++) { 194 fr->in[i] = 0.; 195 if (i < fr->gotsamples) { 196 /* average the two channels */ 197 fr->in[i] = fr->buf[i * 2 + 0]; 198 fr->in[i] += fr->buf[i * 2 + 1]; 199 fr->in[i] /= 2.; 200 } 201 } 202 } 203 204 static void 205 normalizeaudio(struct frame *fr) 206 { 207 size_t i; 208 209 for (i = 0; i < nsamples; i++) 210 fr->in[i] /= (float)INT16_MAX; 211 } 212 213 static void 214 computedft(struct frame *fr) 215 { 216 size_t i; 217 218 fftw_execute(fr->plan); 219 220 for (i = 0; i < dftout; i++) { 221 /* complex absolute value */ 222 fr->res[i] = cabs(fr->out[i]); 223 /* normalize it */ 224 fr->res[i] /= dftlen; 225 } 226 } 227 228 static void 229 setcolor(int on, int y) 230 { 231 size_t i; 232 struct color_range *cr; 233 234 if (!colors) 235 return; 236 237 for (i = 0; i < LEN(color_ranges); i++) { 238 cr = &color_ranges[i]; 239 if (y >= cr->scaled_min && y < cr->scaled_max) { 240 if (on) 241 attron(COLOR_PAIR(cr->pair)); 242 else 243 attroff(COLOR_PAIR(cr->pair)); 244 return; 245 } 246 } 247 } 248 249 static void 250 draw_spectrum(struct frame *fr) 251 { 252 size_t i, j; 253 unsigned freqs_per_col; 254 struct color_range *cr; 255 256 /* read dimensions to catch window resize */ 257 fr->width = COLS; 258 fr->height = LINES; 259 260 if (peaks) { 261 /* change in width needs new peaks */ 262 if (fr->width != fr->width_old) { 263 fr->peak = realloc(fr->peak, fr->width * sizeof(int)); 264 for (i = 0; i < fr->width; i++) 265 fr->peak[i] = PK_HIDDEN; 266 fr->width_old = fr->width; 267 } 268 } 269 270 if (colors) { 271 /* scale color ranges */ 272 for (i = 0; i < LEN(color_ranges); i++) { 273 cr = &color_ranges[i]; 274 cr->scaled_min = cr->min * fr->height / 100; 275 cr->scaled_max = cr->max * fr->height / 100; 276 } 277 } 278 279 /* take most of the low part of the band */ 280 freqs_per_col = dftout / fr->width; 281 freqs_per_col *= 0.8; 282 283 erase(); 284 285 /* scale each frequency to screen */ 286 for (i = 0; i < dftout; i++) { 287 if (debug && i == fr->width / 4) 288 printw("%f\n", fr->res[i]); 289 /* boost higher freqs */ 290 fr->res[i] *= log10(1 + i); 291 fr->res[i] *= 0.25 * i; 292 fr->res[i] = pow(fr->res[i], 0.75); 293 /* scale it */ 294 fr->res[i] *= fr->height; 295 } 296 297 attron(A_BOLD); 298 for (i = 0; i < fr->width; i++) { 299 float bar_height = 0; 300 size_t ybegin, yend; 301 302 /* compute bar height */ 303 for (j = 0; j < freqs_per_col; j++) 304 bar_height += fr->res[i * freqs_per_col + j]; 305 bar_height /= freqs_per_col; 306 307 /* we draw from top to bottom */ 308 ybegin = fr->height - MIN(bar_height, fr->height) + 1; 309 yend = fr->height; 310 311 /* If the current freq reaches the peak, the peak is 312 * updated to that height, else it drops by one line. */ 313 if (peaks) { 314 if (fr->peak[i] >= ybegin) 315 fr->peak[i] = ybegin; 316 else 317 fr->peak[i]++; 318 /* this freq died out */ 319 if (fr->height == ybegin && fr->peak[i] == ybegin) 320 fr->peak[i] = PK_HIDDEN; 321 } 322 323 /* output bars */ 324 for (j = ybegin; j < yend; j++) { 325 move(j, i); 326 setcolor(1, j); 327 printw("%lc", chbar); 328 setcolor(0, j); 329 } 330 331 /* output peaks */ 332 if (peaks && fr->peak[i] != PK_HIDDEN) { 333 move(fr->peak[i], i); 334 setcolor(1, fr->peak[i]); 335 printw("%lc", chpeak); 336 setcolor(0, fr->peak[i]); 337 } 338 } 339 attroff(A_BOLD); 340 refresh(); 341 } 342 343 static void 344 draw_wave(struct frame *fr) 345 { 346 size_t i, j; 347 unsigned samples_per_col; 348 double pt_pos, pt_pos_prev = 0, pt_pos_mid; 349 350 /* read dimensions to catch window resize */ 351 fr->width = COLS; 352 fr->height = LINES; 353 354 erase(); 355 356 /* not enough samples */ 357 if (fr->gotsamples < fr->width) 358 return; 359 360 samples_per_col = fr->gotsamples / fr->width; 361 362 attron(A_BOLD); 363 for (i = 0; i < fr->width; i++) { 364 size_t y; 365 366 /* compute point position */ 367 pt_pos = 0; 368 for (j = 0; j < samples_per_col; j++) 369 pt_pos += fr->in[i * samples_per_col + j]; 370 pt_pos /= samples_per_col; 371 if (debug && i == 0) 372 printw("%+f\n", pt_pos); 373 /* scale it */ 374 pt_pos *= (fr->height / 2) * 0.8; 375 376 /* output points */ 377 y = fr->height / 2 + pt_pos; /* centering */ 378 move(y, i); 379 printw("%lc", chpoint); 380 381 /* Output a helper point by averaging with the previous 382 * position. This creates a nice effect. We don't care 383 * about overlaps with the current point. */ 384 pt_pos_mid = (pt_pos_prev + pt_pos) / 2.0; 385 y = fr->height / 2 + pt_pos_mid; /* centering */ 386 move(y, i); 387 printw("%lc", chpoint); 388 389 pt_pos_prev = pt_pos; 390 } 391 attroff(A_BOLD); 392 refresh(); 393 } 394 395 static void 396 draw_fountain(struct frame *fr) 397 { 398 size_t i, j; 399 struct color_range *cr; 400 static unsigned col = 0; 401 float bar_height = 0; 402 unsigned freqs; 403 404 /* read dimensions to catch window resize */ 405 fr->width = COLS; 406 fr->height = LINES; 407 408 /* change in width needs new keep state */ 409 if (fr->width != fr->width_old) { 410 fr->sav = realloc(fr->sav, fr->width * sizeof(int)); 411 for (i = 0; i < fr->width; i++) 412 fr->sav[i] = fr->height; 413 fr->width_old = fr->width; 414 } 415 416 if (colors) { 417 /* scale color ranges */ 418 for (i = 0; i < LEN(color_ranges); i++) { 419 cr = &color_ranges[i]; 420 cr->scaled_min = cr->min * fr->height / 100; 421 cr->scaled_max = cr->max * fr->height / 100; 422 } 423 } 424 425 for (i = 0; i < dftout; i++) { 426 /* boost higher freqs */ 427 fr->res[i] *= log10(1 + i); 428 fr->res[i] *= 0.25 * i; 429 fr->res[i] = pow(fr->res[i], 0.75); 430 } 431 432 freqs = dftout / fr->width; 433 434 /* compute bar height */ 435 for (j = 0; j < freqs; j++) 436 bar_height += fr->res[j]; 437 bar_height /= freqs; 438 439 erase(); 440 441 if (debug) 442 printw("%f\n", bar_height); 443 444 /* scale it */ 445 bar_height *= fr->height * 5; 446 447 attron(A_BOLD); 448 449 /* ensure we are inside the frame */ 450 col %= fr->width; 451 452 for (i = 0; i < fr->width; i++) { 453 size_t ybegin, yend; 454 455 /* we draw from top to bottom */ 456 if (i == col) { 457 ybegin = fr->height - MIN(bar_height, fr->height); 458 fr->sav[col] = ybegin; 459 } else { 460 if (keep) 461 ybegin = fr->sav[i]; 462 else 463 ybegin = fr->sav[i]++; 464 } 465 yend = fr->height; 466 467 /* output bars */ 468 for (j = ybegin; j < yend; j++) { 469 move(j, i); 470 setcolor(1, j); 471 printw("%lc", chbar); 472 setcolor(0, j); 473 } 474 } 475 476 /* current column bounces back */ 477 if (bounce) 478 if (left) 479 if (col == 0) 480 left = 0; 481 else 482 col--; 483 else 484 if (col == fr->width - 1) 485 left = 1; 486 else 487 col++; 488 /* current column wraps around */ 489 else 490 if (left) 491 col = (col == 0) ? fr->width - 1 : col - 1; 492 else 493 col = (col == fr->width - 1) ? 0 : col + 1; 494 495 attroff(A_BOLD); 496 refresh(); 497 } 498 499 static void 500 draw_boom(struct frame *fr) 501 { 502 size_t i, j; 503 struct color_range *cr; 504 size_t dim, cx, cy; 505 double r, cur; 506 507 erase(); 508 509 /* no samples at all */ 510 if (fr->gotsamples == 0) 511 return; 512 513 /* read dimensions to catch window resize */ 514 fr->width = COLS; 515 fr->height = LINES; 516 517 if (colors) { 518 /* scale color ranges */ 519 for (i = 0; i < LEN(color_ranges); i++) { 520 cr = &color_ranges[i]; 521 cr->scaled_min = cr->min * fr->height / 100; 522 cr->scaled_max = cr->max * fr->height / 100; 523 } 524 } 525 526 /* We assume that to draw a circle using a monospace font we need 527 * _twice_ the distance on the x-axis, so we double everything. */ 528 529 /* size of radius */ 530 dim = MIN(fr->width / 2, fr->height); 531 532 for (i = 0; i < fr->gotsamples; i++) 533 r += fabs(fr->in[i]); 534 r /= fr->gotsamples; 535 if (debug) 536 printw("%f\n", r); 537 /* scale it to our box */ 538 r *= dim; 539 540 /* center */ 541 cx = fr->width / 2; 542 cy = fr->height / 2; 543 544 attron(A_BOLD); 545 setcolor(1, fr->height - 3 * r); 546 /* put the center point */ 547 move(cy, cx); 548 printw("%lc", chpoint); 549 for (i = 0; i < fr->width; i++) { 550 for (j = 0; j < fr->height; j++) { 551 cur = sqrt((i - cx) * (i - cx) + 552 (j - cy) * (j - cy)); 553 /* draw points on the perimeter */ 554 if (cur >= r && cur < r + 1) { 555 move(j, 2 * i - cx); 556 printw("%lc", chpoint); 557 /* leave just the center point alone */ 558 if (i == cx && j == cy) 559 continue; 560 /* draw second point to make line thicker */ 561 if (i <= cx) { 562 move(j, 2 * i - cx - 1); 563 printw("%lc", chpoint); 564 } 565 if (i >= cx) { 566 move(j, 2 * i - cx + 1); 567 printw("%lc", chpoint); 568 } 569 } 570 } 571 } 572 setcolor(0, fr->height - 3 * r); 573 attroff(A_BOLD); 574 refresh(); 575 } 576 577 static void 578 draw_solid(struct frame *fr) 579 { 580 size_t i, j; 581 struct color_range *cr; 582 size_t samples_per_col; 583 double pt_pos, pt_l, pt_r; 584 size_t y; 585 586 /* read dimensions to catch window resize */ 587 fr->width = COLS; 588 fr->height = LINES; 589 590 if (colors) { 591 /* scale color ranges */ 592 for (i = 0; i < LEN(color_ranges); i++) { 593 cr = &color_ranges[i]; 594 cr->scaled_min = cr->min * (fr->height / 2) / 100; 595 cr->scaled_max = cr->max * (fr->height / 2) / 100; 596 } 597 } 598 599 erase(); 600 601 /* not enough samples */ 602 if (fr->gotsamples < fr->width) 603 return; 604 605 samples_per_col = fr->gotsamples / fr->width; 606 607 attron(A_BOLD); 608 for (i = 0; i < fr->width; i++) { 609 /* compute point position */ 610 if (stereo) { 611 /* round down to an even */ 612 if (samples_per_col % 2 != 0) 613 samples_per_col--; 614 pt_l = pt_r = 0; 615 for (j = 0; j < samples_per_col; j += 2) { 616 pt_l += fr->in[i * samples_per_col + j + 0]; 617 pt_r += fr->in[i * samples_per_col + j + 1]; 618 } 619 pt_l /= samples_per_col / 2; 620 pt_r /= samples_per_col / 2; 621 } else { 622 pt_pos = 0; 623 for (j = 0; j < samples_per_col; j++) 624 pt_pos += fr->in[i * samples_per_col + j]; 625 pt_pos /= samples_per_col; 626 /* treat left and right as the same */ 627 pt_l = pt_pos; 628 pt_r = pt_pos; 629 } 630 631 if (debug && i == 0) 632 printw("%+f\n%+f\n", pt_l, pt_r); 633 634 /* scale it */ 635 pt_l *= (fr->height / 2); 636 pt_r *= (fr->height / 2); 637 638 /* output points */ 639 setcolor(1, fr->height / 2 - MAX(abs(pt_l), abs(pt_r))); 640 for (y = fr->height / 2 - abs(pt_l); 641 y <= fr->height / 2 + abs(pt_r); 642 y++) { 643 move(y, i); 644 printw("%lc", chbar); 645 } 646 setcolor(0, fr->height / 2 - MAX(abs(pt_l), abs(pt_r))); 647 } 648 attroff(A_BOLD); 649 refresh(); 650 } 651 652 static void 653 draw_spectro(struct frame *fr) 654 { 655 size_t i, j; 656 unsigned freqs_per_row; 657 static unsigned col = 0; 658 float amplitude = 0; 659 size_t idx; 660 661 /* read dimensions to catch window resize */ 662 fr->width = COLS; 663 fr->height = LINES; 664 665 /* reset on window resize */ 666 if (fr->width != fr->width_old || fr->height != fr->height_old) { 667 erase(); 668 fr->width_old = fr->width; 669 fr->height_old = fr->height; 670 col = 0; 671 } 672 673 /* take most of the low part of the band */ 674 freqs_per_row = dftout / fr->width; 675 676 /* normalize each frequency */ 677 for (i = 0; i < dftout; i++) { 678 /* boost higher freqs */ 679 fr->res[i] *= log10(1 + i); 680 fr->res[i] = pow(fr->res[i], 0.5); 681 /* scale it */ 682 fr->res[i] *= WCSLEN(intensity) * 8.0; 683 } 684 685 /* ensure we are inside the frame */ 686 col %= fr->width; 687 688 attron(A_BOLD); 689 for (j = 0; j < fr->height; j++) { 690 amplitude = 0; 691 /* compute amplitude */ 692 for (i = 0; i < freqs_per_row; i++) 693 amplitude += fr->res[j * freqs_per_row + i]; 694 amplitude /= freqs_per_row; 695 idx = amplitude; 696 if (idx > WCSLEN(intensity)) 697 idx = WCSLEN(intensity) - 1; 698 699 /* output intensity */ 700 move(fr->height - j - 1, col); 701 printw("%lc", intensity[idx]); 702 if (debug) 703 printw(" %0f ", amplitude); 704 } 705 attroff(A_BOLD); 706 refresh(); 707 708 col = (col == fr->width - 1) ? 0 : col + 1; 709 } 710 711 static void 712 initcolors(void) 713 { 714 size_t i; 715 struct color_range *cr; 716 717 start_color(); 718 for (i = 0; i < LEN(color_ranges); i++) { 719 cr = &color_ranges[i]; 720 init_pair(cr->pair, cr->fg, cr->bg); 721 } 722 } 723 724 static void 725 usage(void) 726 { 727 fprintf(stderr, "usage: %s [-hcpklbsd] [-i num] [fifo]\n", argv0); 728 fprintf(stderr, "default fifo path is `/tmp/audio.fifo'\n"); 729 exit(1); 730 } 731 732 int 733 main(int argc, char *argv[]) 734 { 735 int c; 736 struct frame fr; 737 int vidx_prev; 738 int fd; 739 740 argv0 = argv[0]; 741 while (--argc > 0 && (*++argv)[0] == '-') 742 while ((c = *++argv[0])) 743 switch (c) { 744 case 'i': 745 if (*++argv == NULL) 746 usage(); 747 argc--; 748 vidx = *argv[0] - '0' - 1; 749 if (vidx < 0 || vidx > LEN(visuals) - 1) 750 errx(1, "illegal visual index"); 751 break; 752 case 'c': 753 colors = 1; 754 break; 755 case 'p': 756 peaks = 1; 757 break; 758 case 'k': 759 keep = 1; 760 break; 761 case 'l': 762 left = 1; 763 break; 764 case 'b': 765 bounce = 1; 766 break; 767 case 's': 768 stereo = 1; 769 break; 770 case 'd': 771 debug = 1; 772 break; 773 case 'h': 774 /* fall-through */ 775 default: 776 usage(); 777 } 778 if (argc == 1) 779 fname = argv[0]; 780 else if (argc > 1) 781 usage(); 782 783 /* init frame context */ 784 memset(&fr, 0, sizeof(fr)); 785 init(&fr); 786 787 setlocale(LC_ALL, ""); 788 789 /* init curses */ 790 initscr(); 791 cbreak(); 792 noecho(); 793 nonl(); 794 intrflush(stdscr, FALSE); 795 keypad(stdscr, TRUE); 796 curs_set(FALSE); /* hide cursor */ 797 timeout(msec); 798 use_default_colors(); 799 800 if (colors && has_colors() == FALSE) { 801 endwin(); 802 done(&fr); 803 errx(1, "your terminal does not support colors"); 804 } 805 806 vidx_prev = vidx; 807 808 while (!die) { 809 switch (getch()) { 810 case 'q': 811 die = 1; 812 break; 813 case 'c': 814 if (has_colors() == TRUE) 815 colors = !colors; 816 break; 817 case 'p': 818 peaks = !peaks; 819 break; 820 case 'k': 821 keep = !keep; 822 break; 823 case 'l': 824 left = !left; 825 break; 826 case 'b': 827 bounce = !bounce; 828 break; 829 case 's': 830 stereo = !stereo; 831 break; 832 case '1': 833 vidx = 0; 834 break; 835 case '2': 836 vidx = 1; 837 break; 838 case '3': 839 vidx = 2; 840 break; 841 case '4': 842 vidx = 3; 843 break; 844 case '5': 845 vidx = 4; 846 break; 847 case '6': 848 vidx = 5; 849 break; 850 case 'n': 851 case KEY_RIGHT: 852 vidx = vidx == (LEN(visuals) - 1) ? 0 : vidx + 1; 853 break; 854 case 'N': 855 case KEY_LEFT: 856 vidx = vidx == 0 ? LEN(visuals) - 1 : vidx - 1; 857 break; 858 case ' ': 859 freeze = !freeze; 860 break; 861 case 'd': 862 debug = !debug; 863 break; 864 } 865 866 /* detect visualization change */ 867 if (vidx != vidx_prev) 868 fr.width_old = 0; 869 870 /* not all visuals support colors */ 871 if (colors && visuals[vidx].color) 872 initcolors(); 873 else 874 (void)use_default_colors(); 875 876 update(&fr); 877 878 /* may need to merge channels */ 879 if (stereo && visuals[vidx].stereo) 880 stagestereo(&fr); 881 else 882 stagemono(&fr); 883 884 normalizeaudio(&fr); 885 886 /* compute the DFT if needed */ 887 if (visuals[vidx].dft) 888 computedft(&fr); 889 890 if (!freeze) 891 visuals[vidx].draw(&fr); 892 893 vidx_prev = vidx; 894 } 895 896 endwin(); /* restore terminal */ 897 898 done(&fr); /* destroy context */ 899 900 return (0); 901 }