ncmixer

ncurses audio mixer for DJ'ing
git clone git://git.2f30.org/ncmixer.git
Log | Files | Refs | README | LICENSE

ncmixerc.c (8015B)


      1 #include <sys/types.h>
      2 #include <sys/socket.h>
      3 #include <sys/un.h>
      4 
      5 #include <curses.h>
      6 #include <err.h>
      7 #include <errno.h>
      8 #include <math.h>
      9 #include <signal.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 #include <unistd.h>
     14 
     15 #include "proto.h"
     16 #include "util.h"
     17 
     18 #define LEN(x) (sizeof (x) / sizeof *(x))
     19 #undef MIN
     20 #define MIN(x, y) ((x) < (y) ? (x) : (y))
     21 #undef MAX
     22 #define SA struct sockaddr
     23 #define MAX(x, y) ((x) > (y) ? (x) : (y))
     24 #define ISODD(x) ((x) & 1)
     25 #define CONTROL(c) ((c) ^ 0x40)
     26 #define CTL_SOCK_PATH "/tmp/mixerctl"
     27 #define TIMEO_MS 40
     28 
     29 #ifdef DEBUG
     30 #define DEBUG_FD 8
     31 #define DPRINTF(x...) dprintf(DEBUG_FD, x)
     32 #define DPRINTF_D(x) dprintf(DEBUG_FD, #x "=%d\n", x)
     33 #define DPRINTF_F(x) dprintf(DEBUG_FD, #x "=%0.2f\n", x)
     34 #else
     35 #define DPRINTF
     36 #define DPRINTF_D(x)
     37 #define DPRINTF_F(x)
     38 #endif /* DEBUG */
     39 
     40 float xf_pos = 0.0f;
     41 float step = 0.0625f;
     42 float level_step = 0.125f;
     43 float ch0_level = 1.0f;
     44 float ch1_level = 1.0f;
     45 int mon0_on;
     46 int mon1_on;
     47 int in0_on;
     48 int in1_on;
     49 int out0_on;
     50 int out1_on;
     51 int air_on;
     52 int sock;
     53 
     54 void
     55 curses_init(void)
     56 {
     57 	char *term;
     58 
     59 	if (initscr() == NULL) {
     60 		term = getenv("TERM");
     61 		if (term != NULL)
     62 			errx(1, "error opening terminal: %s", term);
     63 		else
     64 			errx(1, "failed to initialize curses");
     65 	}
     66 	cbreak();
     67 	noecho();
     68 	nonl();
     69 	intrflush(stdscr, FALSE);
     70 	curs_set(FALSE);
     71 	timeout(TIMEO_MS);
     72 }
     73 
     74 void
     75 curses_exit(void)
     76 {
     77 	endwin();
     78 }
     79 
     80 void
     81 tx_msg(struct msg *msg)
     82 {
     83 	int n;
     84 
     85 	do {
     86 		n = write(sock, msg, sizeof(*msg));
     87 	} while (n == -1 && errno == EINTR);
     88 	if (n == -1) {
     89 		curses_exit();
     90 		errx(1, "lost connection to daemon");
     91 	}
     92 }
     93 
     94 void
     95 rx_msg(struct msg *msg)
     96 {
     97 	int n;
     98 
     99 	do {
    100 		n = read(sock, msg, sizeof(*msg));
    101 	} while (n == -1 && errno == EINTR);
    102 	if (n == 0 || n == -1) {
    103 		curses_exit();
    104 		errx(1, "lost connection to daemon");
    105 	}
    106 }
    107 
    108 void
    109 get_xf_pos(float *xf_pos)
    110 {
    111 	struct msg msg;
    112 
    113 	msg.type = GET_XF;
    114 	tx_msg(&msg);
    115 	rx_msg(&msg);
    116 	*xf_pos = msg.m_xf_pos;
    117 }
    118 
    119 void
    120 set_xf_pos(float xf_pos)
    121 {
    122 	struct msg msg;
    123 
    124 	msg.type = SET_XF;
    125 	msg.m_xf_pos = xf_pos;
    126 	tx_msg(&msg);
    127 }
    128 
    129 void
    130 get_mon_on(int index, int *on)
    131 {
    132 	struct msg msg;
    133 
    134 	msg.type = GET_MON;
    135 	msg.m_mon_index = index;
    136 	tx_msg(&msg);
    137 	rx_msg(&msg);
    138 	*on = msg.m_mon_on;
    139 }
    140 
    141 void
    142 set_mon_on(int index, int on)
    143 {
    144 	struct msg msg;
    145 
    146 	msg.type = SET_MON;
    147 	msg.m_mon_index = index;
    148 	msg.m_mon_on = on;
    149 	tx_msg(&msg);
    150 }
    151 
    152 void
    153 get_vol_level(int chan, float *level)
    154 {
    155 	struct msg msg;
    156 
    157 	msg.type = GET_VOL;
    158 	msg.m_vol_chan = chan;
    159 	tx_msg(&msg);
    160 	rx_msg(&msg);
    161 	*level = msg.m_vol_level;
    162 }
    163 
    164 void
    165 set_vol_level(int chan, float level)
    166 {
    167 	struct msg msg;
    168 
    169 	msg.type = SET_VOL;
    170 	msg.m_vol_chan = chan;
    171 	msg.m_vol_level = level;
    172 	tx_msg(&msg);
    173 }
    174 
    175 void
    176 get_input_on(int index, int *on)
    177 {
    178 	struct msg msg;
    179 
    180 	msg.type = GET_IN;
    181 	msg.m_input_index = index;
    182 	tx_msg(&msg);
    183 	rx_msg(&msg);
    184 	*on = msg.m_input_on;
    185 }
    186 
    187 void
    188 get_output_on(int index, int *on)
    189 {
    190 	struct msg msg;
    191 
    192 	msg.type = GET_OUT;
    193 	msg.m_output_index = index;
    194 	tx_msg(&msg);
    195 	rx_msg(&msg);
    196 	*on = msg.m_output_on;
    197 }
    198 
    199 void
    200 get_air_on(int *on)
    201 {
    202 	struct msg msg;
    203 
    204 	msg.type = GET_AIR;
    205 	tx_msg(&msg);
    206 	rx_msg(&msg);
    207 	*on = msg.m_air_on;
    208 }
    209 
    210 void
    211 set_air_on(int on)
    212 {
    213 	struct msg msg;
    214 
    215 	msg.type = SET_AIR;
    216 	msg.m_air_on = on;
    217 	tx_msg(&msg);
    218 }
    219 
    220 void
    221 kb_handler(void)
    222 {
    223 	int c;
    224 
    225 	c = getch();
    226 	DPRINTF_D(c);
    227 	switch (c) {
    228 	case 'h':
    229 		get_xf_pos(&xf_pos);
    230 		xf_pos = MAX(xf_pos - step, -1.0f);
    231 		set_xf_pos(xf_pos);
    232 		break;
    233 	case 'l':
    234 		get_xf_pos(&xf_pos);
    235 		xf_pos = MIN(xf_pos + step, 1.0f);
    236 		set_xf_pos(xf_pos);
    237 		break;
    238 	case 'j':
    239 		get_xf_pos(&xf_pos);
    240 		step = MAX(step / 2, 0.015625f);
    241 		set_xf_pos(xf_pos);
    242 		break;
    243 	case 'k':
    244 		get_xf_pos(&xf_pos);
    245 		step = MIN(step * 2, 0.25f);
    246 		set_xf_pos(xf_pos);
    247 		break;
    248 	case CONTROL('H'):
    249 		get_xf_pos(&xf_pos);
    250 		if (xf_pos > 0.)
    251 			xf_pos = 0.f;
    252 		else
    253 			xf_pos = -1.f;
    254 		set_xf_pos(xf_pos);
    255 		break;
    256 	case CONTROL('L'):
    257 		get_xf_pos(&xf_pos);
    258 		if (xf_pos < 0.)
    259 			xf_pos = 0.f;
    260 		else
    261 			xf_pos = 1.f;
    262 		set_xf_pos(xf_pos);
    263 		break;
    264 	case '1':
    265 		get_mon_on(0, &mon0_on);
    266 		mon0_on = !mon0_on;
    267 		set_mon_on(0, mon0_on);
    268 		break;
    269 	case '2':
    270 		get_mon_on(1, &mon1_on);
    271 		mon1_on = !mon1_on;
    272 		set_mon_on(1, mon1_on);
    273 		break;
    274 	case 'q':
    275 		get_vol_level(0, &ch0_level);
    276 		ch0_level = MAX(ch0_level - level_step, 0.0f);
    277 		set_vol_level(0, ch0_level);
    278 		break;
    279 	case 'w':
    280 		get_vol_level(0, &ch0_level);
    281 		ch0_level = MIN(ch0_level + level_step, 1.0f);
    282 		set_vol_level(0, ch0_level);
    283 		break;
    284 	case 'o':
    285 		get_vol_level(1, &ch1_level);
    286 		ch1_level = MAX(ch1_level - level_step, 0.0f);
    287 		set_vol_level(1, ch1_level);
    288 		break;
    289 	case 'p':
    290 		get_vol_level(1, &ch1_level);
    291 		ch1_level = MIN(ch1_level + level_step, 1.0f);
    292 		set_vol_level(1, ch1_level);
    293 		break;
    294 	case CONTROL('A'):
    295 		get_air_on(&air_on);
    296 		air_on = !air_on;
    297 		set_air_on(air_on);
    298 		break;
    299 	case 'x':
    300 		curses_exit();
    301 		exit(0);
    302 	}
    303 }
    304 
    305 void
    306 draw_status(void)
    307 {
    308 	int offs;
    309 
    310 	offs = ISODD(COLS) ? 2 : 3;
    311 	if (COLS < 2 + strlen("[CH0: ] [MON: ] [CH1: ] [MON: ]") + offs) {
    312 		move(getcury(stdscr), 0);
    313 		printw("\n");
    314 		return;
    315 	}
    316 
    317 	get_input_on(0, &in0_on);
    318 	get_input_on(1, &in1_on);
    319 	get_mon_on(0, &mon0_on);
    320 	get_mon_on(1, &mon1_on);
    321 
    322 	move(getcury(stdscr), 2);
    323 	printw("[CH0:%c] [MON:%c]",
    324 	       in0_on ? '*' : ' ',
    325 	       mon0_on ? '*' : ' ');
    326 	move(getcury(stdscr), COLS - strlen("[MON: ] [CH1: ]") - offs);
    327 	printw("[CH1:%c] [MON:%c]",
    328 	       in1_on ? '*' : ' ',
    329 	       mon1_on ? '*' : ' ');
    330 	printw("\n");
    331 }
    332 
    333 void
    334 draw_levels(void)
    335 {
    336 	int start, end, len, nsteps, i;
    337 
    338 	start = 2;
    339 	end = ISODD(COLS) ? COLS - 2 : COLS - 3;
    340 	len = 1.0f / level_step;
    341 
    342 	get_vol_level(0, &ch0_level);
    343 	get_vol_level(1, &ch1_level);
    344 
    345 	move(getcury(stdscr), start);
    346 	nsteps = ch0_level / level_step;
    347 	for (i = 0; i < nsteps; i++)
    348 		printw("|");
    349 	nsteps = ch1_level / level_step;
    350 	move(getcury(stdscr), end - len);
    351 	for (i = 0; i < nsteps; i++)
    352 		printw("|");
    353 	printw("\n");
    354 }
    355 
    356 void
    357 draw_xfader(void)
    358 {
    359 	int start, end, len, center, pos, i;
    360 
    361 	if (COLS < strlen("  -|x|-  "))
    362 		return;
    363 
    364 	get_xf_pos(&xf_pos);
    365 
    366 	start = 2;
    367 	end = ISODD(COLS) ? COLS - 2 : COLS - 3;
    368 	len = end - start;
    369 	center = start + (len / 2);
    370 	if (xf_pos <= 0.0f)
    371 		pos = center - ceil((len / 2) * fabsf(xf_pos));
    372 	else
    373 		pos = center + ceil((len / 2) * fabsf(xf_pos));
    374 
    375 	move(getcury(stdscr), center - strlen("[step: 0.000000") / 2);
    376 	printw("step: %0.6f\n", step);
    377 	move(getcury(stdscr), center - strlen("[pos: +0.000000") / 2);
    378 	printw("pos: %+0.6f\n", xf_pos);
    379 	printw("\n");
    380 
    381 	move(getcury(stdscr), start);
    382 	for (i = 0; i < len; i++)
    383 		printw("-");
    384 	move(getcury(stdscr), center);
    385 	printw("+");
    386 	move(getcury(stdscr), pos - 1);
    387 	if (pos == center)
    388 		printw("|x|");
    389 	else
    390 		printw("|||");
    391 	move(getcury(stdscr), end + 1);
    392 	printw("\n");
    393 }
    394 
    395 void
    396 draw_outputs(void)
    397 {
    398 	int start, end, len, center;
    399 
    400 	start = 2;
    401 	end = ISODD(COLS) ? COLS - 2 : COLS - 3;
    402 	len = end - start;
    403 	center = start + (len / 2);
    404 
    405 	get_output_on(0, &out0_on);
    406 	get_output_on(1, &out1_on);
    407 	get_air_on(&air_on);
    408 
    409 	move(getcury(stdscr), center - strlen(" master: off") / 2);
    410 	printw(" master: %s\n", out0_on ? "on" : "off");
    411 	move(getcury(stdscr), center - strlen("monitor: off") / 2);
    412 	printw("monitor: %s\n", out1_on ? "on" : "off");
    413 	move(getcury(stdscr), center - strlen("    air: off") / 2);
    414 	printw("  onair: %s\n", air_on ? "on" : "off");
    415 }
    416 
    417 void
    418 draw(void)
    419 {
    420 	erase();
    421 	draw_status();
    422 	draw_xfader();
    423 	draw_levels();
    424 	draw_outputs();
    425 	refresh();
    426 }
    427 
    428 void
    429 loop(void)
    430 {
    431 	for (;;) {
    432 		draw();
    433 		kb_handler();
    434 	}
    435 }
    436 
    437 int
    438 client_connect(char *name)
    439 {
    440 	struct sockaddr_un sun;
    441 	socklen_t len;
    442 	int fd;
    443 
    444 	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
    445 	if (fd == -1)
    446 		err(1, "socket");
    447 	sun.sun_family = AF_UNIX;
    448 	strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
    449 	len = sizeof(sun);
    450 	if (connect(fd, (SA *)&sun, len) == -1)
    451 		errx(1, "cannot connect to daemon");
    452 	return fd;
    453 }
    454 
    455 int
    456 main(void)
    457 {
    458 	if (isatty(0) == 0 || isatty(1) == 0)
    459 		errx(1, "stdin or stdout is not a tty");
    460 	signal(SIGPIPE, SIG_IGN);
    461 	sock = client_connect(CTL_SOCK_PATH);
    462 	curses_init();
    463 	loop();
    464 	curses_exit();
    465 	return 0;
    466 }