nausea

curses audio visualizer
git clone git://git.2f30.org/nausea
Log | Files | Refs | README | LICENSE

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 }