amp

pcm player/editor for plan9
git clone git://git.nsmpr.xyz/amp.git
Log | Files | Refs | README

amp.c (13044B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <draw.h>
      4 #include <thread.h>
      5 #include <mouse.h>
      6 #include <cursor.h>
      7 #include <keyboard.h>
      8 #include <plumb.h>
      9 
     10 enum {
     11 	FrameSize = sizeof(s16int) * 2,
     12 	PBufLen = 1024 * FrameSize,
     13 	DHeight = 32,
     14 	MaskCacheSize = 256,
     15 	ScrollBarWidth = 12,
     16 	Margin = 4,
     17 
     18 	PStop = 0,
     19 	PPlay = 1,
     20 	PPause = 2,
     21 };
     22 
     23 Image * getmask(int);
     24 long decoord(Point);
     25 int row(int);
     26 void clearcursor(void);
     27 void clearmask(void);
     28 void drawcursor(void);
     29 void drawmask(Image *, int);
     30 void drawpcm(Point, int);
     31 void drawscroll(int);
     32 void drawscrollbar(void);
     33 void loadpcm(int);
     34 void mouseidle(Mouse);
     35 void mousescroll(Mouse);
     36 void mouseselect(Mouse);
     37 void mexit(void);
     38 void mplumb(void);
     39 void mredraw(void);
     40 void msnarf(void);
     41 void mwrite(void);
     42 void mzoom(void);
     43 void redraw(void);
     44 void resize(void);
     45 void setselect(long, long);
     46 void threadflush(void *);
     47 void threadplay(void *);
     48 void threadplumb(void *);
     49 void usage(void);
     50 
     51 long monolen, scroll, smin, smax, ss, se, Zoomout = 512;
     52 Image *Ibg, *Itrbg, *Itrfg, *Icur;
     53 Rectangle rscroll, rbars;
     54 int dwidth, needflush, maxbars;
     55 char wpath[1024];
     56 Mousectl *mctl;
     57 Keyboardctl *kctl;
     58 char *menustr[] = {"snarf", "plumb", "redraw", "write", "zoom", "exit", nil};
     59 void (*menufunc[])(void) = {msnarf, mplumb, mredraw, mwrite, mzoom, mexit};
     60 void (*mousefp)(Mouse) = mouseidle;
     61 Menu menu = {menustr, nil, 0};
     62 struct {
     63 	int send, recv;
     64 	char *port;
     65 } plumb = {.port = "amp"};
     66 struct {
     67 	s8int *p;
     68 	int fid, len, mtime;
     69 	char *path;
     70 } pcm;
     71 struct {
     72 	char *path;
     73 	s8int *p;
     74 	int fid, state;
     75 	vlong len, cur;
     76 } play = {.path = "/dev/audio"};
     77 struct {
     78 	Image *img[MaskCacheSize];
     79 	int id[MaskCacheSize];
     80 } mask;
     81 
     82 void
     83 threadmain(int argc, char **argv)
     84 {
     85 	Mouse mv;
     86 	Rune  kv;
     87 	int   rv[2];
     88 
     89 	ARGBEGIN{
     90 	default:
     91 		usage();
     92 	}ARGEND;
     93 
     94 	if (argc == 0) usage();
     95 	pcm.path = argv[0];
     96 	pcm.fid = open(pcm.path, OREAD);
     97 	loadpcm(pcm.fid);
     98 	monolen = pcm.len / FrameSize;
     99 
    100 	plumb.send = plumbopen("send", OWRITE);
    101 	if (plumb.send < 0) fprint(2, "%r\n");
    102 	plumb.recv = plumbopen(plumb.port, OREAD);
    103 	if (plumb.recv <0) fprint(2, "%r\n");
    104 
    105 	if(initdraw(0, 0, "amp") < 0)
    106 		sysfatal("inidraw failed: %r");
    107 	if((mctl = initmouse(0, screen)) == nil)
    108 		sysfatal("initmouse failed: %r");
    109 	if((kctl = initkeyboard(0)) == nil)
    110 		sysfatal("initkeyboard failed: %r");
    111 
    112 	display->locking = 1;
    113 	unlockdisplay(display);
    114 
    115 	proccreate(threadflush, nil, 1024);
    116 	proccreate(threadplumb, nil, 1024);
    117 	proccreate(threadplay, nil, 1024);
    118 
    119 	lockdisplay(display);
    120 	Ibg = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0xBBBBBBFF);
    121 	Itrbg = allocimage(display, Rect(0,0,1,1), RGB24, 1, DWhite);
    122 	Itrfg = allocimage(display, Rect(0,0,1,1), RGB24, 1, DBlack);
    123 	Icur = allocimage(display, Rect(0,0,1,1), RGB24, 1, DRed);
    124 	resize();
    125 	clearmask();
    126 	unlockdisplay(display);
    127 	redraw();
    128 	Alt alts[5] = {
    129 		{kctl->c, &kv, CHANRCV},
    130 		{mctl->c, &mv, CHANRCV},
    131 		{mctl->resizec, rv, CHANRCV},
    132 		{0, 0, CHANEND},
    133 	};
    134 	for (;;) {
    135 		switch (alt(alts)) {
    136 		case 0: /* keyboard */
    137 			if (kv == 0x7f) threadexitsall(nil);
    138 			if (kv == Kdown) drawscroll(maxbars/3);
    139 			if (kv == Kup) drawscroll(-maxbars/3);
    140 			if (kv == Kpgdown) drawscroll(maxbars);
    141 			if (kv == Kpgup) drawscroll(-maxbars);
    142 			if (kv == ' ') play.state = (play.state == PPlay) ? PPause : PPlay;
    143 			break;
    144 		case 1: /* mouse */
    145 			mousefp(mv);
    146 			break;
    147 		case 2: /* resize */
    148 			lockdisplay(display);
    149 			if(getwindow(display, Refnone) < 0)
    150 				sysfatal("resize failed: %r");
    151 			resize();
    152 			clearmask();
    153 			unlockdisplay(display);
    154 			redraw();
    155 			break;
    156 		}
    157 	}
    158 }
    159 
    160 void
    161 threadflush(void *)
    162 {
    163 	threadsetname("flush");
    164 	for (;;) {
    165 		if (needflush != 0) {
    166 			lockdisplay(display);
    167 			flushimage(display, 1);
    168 			unlockdisplay(display);
    169 			needflush = 0;
    170 		}
    171 		sleep(1000/60);
    172 	}
    173 }
    174 
    175 void
    176 threadplumb(void *)
    177 {
    178 	Plumbmsg *m;
    179 	long s, e;
    180 	if (plumb.recv < 0) return;
    181 	threadsetname("plumb");
    182 	for (;;) {
    183 		m = plumbrecv(plumb.recv);
    184 		if (m->ndata < 24) fprint(2, "plumb message too short\n");
    185 		else if (strcmp(m->src, "amp") != 0){
    186 			s = strtol(m->data, nil, 10);
    187 			e = strtol(m->data + 12, nil, 10);
    188 			setselect(s / (Zoomout * FrameSize), e / (Zoomout * FrameSize));
    189 		}
    190 		plumbfree(m);
    191 	}
    192 }
    193 
    194 void
    195 threadplay(void *)
    196 {
    197 	vlong n, pend;
    198 	if ((play.fid = open(play.path, OWRITE)) < 0) {
    199 		fprint(2, "%r\n");
    200 		return;
    201 	}
    202 	play.p = malloc(PBufLen);
    203 	if (play.p == nil) sysfatal("%r");
    204 	for (;;) {
    205 		switch (play.state) {
    206 		case PStop:
    207 			play.cur = smin * Zoomout * FrameSize;
    208 		case PPause:
    209 			sleep(1000/60);
    210 			break;
    211 		case PPlay:
    212 			pend = (smin != smax) ? smax * Zoomout * FrameSize : pcm.len;
    213 			n = (PBufLen < pend - play.cur) ? PBufLen : pend - play.cur;
    214 			n = pread(pcm.fid, play.p, n, play.cur);
    215 			n = write(play.fid, play.p, n);
    216 			clearcursor();
    217 			play.cur += n;
    218 			drawcursor();
    219 			if (play.cur >= pend) play.state = PStop;
    220 			needflush = 1;
    221 		}
    222 	}
    223 }
    224 
    225 void
    226 mouseidle(Mouse mv)
    227 {
    228 	int n;
    229 	if ((mv.buttons != 0) && (ptinrect(mv.xy, rscroll) != 0)) {
    230 		mousefp = mousescroll;
    231 		mousescroll(mv);
    232 	} else switch(mv.buttons) {
    233 	case 1:
    234 		mousefp = mouseselect;
    235 		ss = decoord(mv.xy) / (Zoomout * FrameSize);
    236 		mouseselect(mv);
    237 		break;
    238 	case 4:
    239 		n = menuhit(3, mctl, &menu, nil);
    240 		if (n >= 0) menufunc[n]();
    241 		break;
    242 	case 8:
    243 		drawscroll(-1-row(mv.xy.y));
    244 		break;
    245 	case 16:
    246 		drawscroll(1+row(mv.xy.y));
    247 		break;
    248 	}
    249 }
    250 
    251 void
    252 mousescroll(Mouse mv)
    253 {
    254 	int tl;
    255 	switch(mv.buttons) {
    256 	case 0:
    257 		mousefp = mouseidle;
    258 		break;
    259 	case 1:
    260 		drawscroll(-1-row(mv.xy.y));
    261 		break;
    262 	case 2:
    263 		tl = pcm.len / (FrameSize * Zoomout * dwidth);
    264 		scroll = tl * (mv.xy.y - rscroll.min.y) / Dy(rscroll);
    265 		if (scroll > tl) scroll = tl;
    266 		if (scroll < 0) scroll = 0;
    267 		redraw();
    268 		needflush = 1;
    269 		break;
    270 	case 4:
    271 		drawscroll(1+row(mv.xy.y));
    272 		break;
    273 	case 8:
    274 		drawscroll(-1-row(mv.xy.y));
    275 		break;
    276 	case 16:
    277 		drawscroll(1+row(mv.xy.y));
    278 		break;
    279 	}
    280 }
    281 
    282 void
    283 mouseselect(Mouse mv)
    284 {
    285 	if (mv.buttons == 0) {
    286 		mousefp = mouseidle;
    287 		return;
    288 	}
    289 	se = decoord(mv.xy) / (Zoomout * FrameSize);
    290 	setselect(ss, se);
    291 }
    292 
    293 void
    294 resize(void)
    295 {
    296 	rscroll = screen->r;
    297 	rscroll.min.x += Margin;
    298 	rscroll.min.y += Margin;
    299 	rscroll.max.x = rscroll.min.x + ScrollBarWidth;
    300 	rscroll.max.y -= Margin;
    301 
    302 	rbars = screen->r;
    303 	rbars.min.x += 2 * Margin + ScrollBarWidth;
    304 	rbars.max.x -= Margin;
    305 	rbars.min.y += Margin;
    306 	rbars.max.y -= Margin;
    307 	dwidth = Dx(rbars);
    308 	if (scroll > pcm.len/(FrameSize * Zoomout * dwidth))
    309 		scroll = pcm.len/(FrameSize * Zoomout * dwidth);
    310 	maxbars = Dy(rbars) / (DHeight + Margin);
    311 }
    312 
    313 void
    314 usage(void)
    315 {
    316 	fprint(2, "usage: %s file.pcm\n", argv0);
    317 	threadexitsall("usage");
    318 }
    319 
    320 Image *
    321 getmask(int n)
    322 {
    323 	int m;
    324 	m = n % MaskCacheSize;
    325 	if (mask.id[m] != n) {
    326 		mask.id[m] = n;
    327 		if (mask.img[m] == nil) {
    328 			mask.img[m] = allocimage(display, Rect(0, 0, dwidth, DHeight),
    329 			  CMAP8, 0, DBlue);
    330 		}
    331 		drawmask(mask.img[m], n);
    332 	}
    333 	return mask.img[m];
    334 }
    335 
    336 void
    337 drawpcm(Point p, int n)
    338 {
    339 	Image *mask;
    340 	Rectangle r;
    341 	long w;
    342 	ulong start;
    343 	mask = getmask(n);
    344 	r.min = p;
    345 	r.max.x = p.x + Dx(mask->clipr);
    346 	r.max.y = p.y + DHeight;
    347 	if (r.max.y > rbars.max.y) r.max.y = rbars.max.y;
    348 	start = n * dwidth;
    349 	lockdisplay(display);
    350 	if (start < smin) {
    351 		w = smin - start;
    352 		if (w > Dx(r)) w = Dx(r);
    353 		r.max.x = r.min.x + w;
    354 		draw(screen, r, mask, 0, ZP);
    355 		start += w;
    356 		r.min.x += w;
    357 		r.max.x = p.x + Dx(mask->clipr);
    358 	}
    359 	if (start < smax) {
    360 		w = smax - start;
    361 		if (w > Dx(r)) w = Dx(r);
    362 		r.max.x = r.min.x + w;
    363 		draw(screen, r, Itrbg, 0, ZP);
    364 		draw(screen, r, Itrfg, mask, Pt(r.min.x - p.x, 0));
    365 		start += w;
    366 		r.min.x += w;
    367 		r.max.x = p.x + Dx(mask->clipr);
    368 	}
    369 	if ((start >= smax)) {
    370 		draw(screen, r, mask, 0, Pt(r.min.x - p.x, 0));
    371 	}
    372 	unlockdisplay(display);
    373 	needflush = 1;
    374 
    375 	return;
    376 }
    377 
    378 void
    379 redraw(void)
    380 {
    381 	int d;
    382 	Point p;
    383 	p = rbars.min;
    384 	lockdisplay(display);
    385 	draw(screen, screen->r, Ibg, nil, ZP);
    386 	unlockdisplay(display);
    387 	drawscrollbar();
    388 	d = scroll;
    389 	while (p.y < screen->r.max.y) {
    390 		if (d * dwidth > pcm.len / (FrameSize * Zoomout)) break;
    391 		drawpcm(p, d);
    392 		d++;
    393 		p.y += DHeight + Margin;
    394 	}
    395 	drawcursor();
    396 }
    397 
    398 void
    399 loadpcm(int fd)
    400 {
    401 	long n;
    402 	s8int *buf;
    403 	buf = malloc(32 * 1024);
    404 	pcm.len = 0;
    405 	while((n = read(fd, buf, 32 * 1024)) > 0){
    406 		pcm.p = realloc(pcm.p, pcm.len + n);
    407 		memcpy(pcm.p + pcm.len, buf, n);
    408 		pcm.len += n;
    409 	}
    410 	free(buf);
    411 }
    412 
    413 void
    414 drawmask(Image *mask, int bn)
    415 {
    416 	Rectangle r;
    417 	int min, max, mono;
    418 	long bsize;
    419 	s8int *rbuf;
    420 	u8int *buf, *bp;
    421 	uint dmin, dmax;
    422 	ulong n, i, j, rlen, FZ, start;
    423 	start = bn * dwidth;
    424 	FZ = FrameSize * Zoomout;
    425 	rbuf = malloc(dwidth * FZ);
    426 	rlen = pread(pcm.fid, rbuf, dwidth * FZ, start * FZ);
    427 	bsize = dwidth * DHeight;
    428 	buf = mallocz(bsize, 1);
    429 	bp = buf;
    430 	min = 0xff;
    431 	max = 0;
    432 	i = 0;
    433 	for (n = 0; (n < rlen) && (bp < buf + dwidth); n += FrameSize) {
    434 		mono = (rbuf[n + 1] + rbuf[n + 3] + 0xff) / 2;
    435 		if (min > mono) min = mono;
    436 		if (max < mono) max = mono;
    437 		if (i >= Zoomout - 1) {
    438 			i = 0;
    439 			dmin = min * DHeight / 256;
    440 			dmax = max * DHeight / 256;
    441 			if (dmin == dmax) dmax++;
    442 			for (j = 0; j < dmin; j++) *(bp + j * dwidth) = 0xff;
    443 			for (j = dmin; j < dmax; j++) *(bp + j * dwidth) = 0x00;
    444 			for (j = dmax; j < Dy(mask->r); j++) *(bp + j * dwidth) = 0xff;
    445 			min = 0xff;
    446 			max = 0;
    447 			bp++;
    448 		} else i++;
    449 	}
    450 	r = mask->r;
    451 	r.max.x = r.min.x + rlen / FZ;
    452 	lockdisplay(display);
    453 	loadimage(mask, mask->r, buf, bsize);
    454 	replclipr(mask, 0, r);
    455 	unlockdisplay(display);
    456 	free(buf);
    457 	free(rbuf);
    458 }
    459 
    460 int
    461 row(int y)
    462 {
    463 	return (y - rbars.min.y) / (DHeight + Margin);
    464 }
    465 
    466 void
    467 drawscroll(int ds)
    468 {
    469 	scroll += ds;
    470 	if (scroll < 0) {
    471 		ds -= scroll;
    472 		scroll = 0;
    473 	}
    474 	if (scroll > monolen / (Zoomout * dwidth)) {
    475 		ds -=  scroll - monolen / (Zoomout * dwidth);
    476 		scroll = monolen / (Zoomout * dwidth);
    477 	}
    478 	if (ds == 0) return;
    479 	redraw();
    480 	needflush = 1;
    481 }
    482 
    483 void
    484 drawscrollbar(void)
    485 {
    486 	Rectangle r, br;
    487 	int tl, offset, width;
    488 	r = rscroll;
    489 
    490 	tl = pcm.len / (FrameSize * Zoomout * dwidth);
    491 
    492 	offset = scroll * Dy(rbars) / tl;
    493 	width = maxbars * Dy(rbars) / tl;
    494 	
    495 	br = Rect(
    496 	  r.min.x,
    497 	  r.min.y + offset,
    498 	  r.max.x,
    499 	  r.min.y + offset + width);
    500 	if (br.max.y > r.max.y) br.max.y = r.max.y;
    501 	lockdisplay(display);
    502 	draw(screen, r, Itrbg, nil, ZP);
    503 	draw(screen, br, Itrfg, nil, ZP);
    504 	unlockdisplay(display);
    505 }
    506 
    507 void
    508 msnarf(void)
    509 {
    510 	int fd, n, min, max;
    511 	char buf[25];
    512 	min = smin * Zoomout * FrameSize;
    513 	max = smax * Zoomout * FrameSize;
    514 	n = snprint(buf, 25, "%11d %11d ", min, max);
    515 	if ((fd = open("/dev/snarf", OWRITE)) < 0) {
    516 		fprint(2, "%r\n");
    517 		return;
    518 	}
    519 	write(fd, buf, n);
    520 	close(fd);
    521 }
    522 
    523 void
    524 mplumb(void)
    525 {
    526 	int min, max;
    527 	min = smin * Zoomout * FrameSize;
    528 	max = smax * Zoomout * FrameSize;
    529 	Plumbmsg *m;
    530 	m = mallocz(sizeof(Plumbmsg), 1);
    531 	m->src = smprint("amp");
    532 	m->dst = strdup(plumb.port);
    533 	m->type = strdup("text");
    534 	m->data = smprint("%11d %11d ", min, max);
    535 	m->ndata = strlen(m->data);
    536 	plumbsend(plumb.send, m);
    537 	plumbfree(m);
    538 }
    539 
    540 void
    541 mredraw(void)
    542 {
    543 	clearmask();
    544 	redraw();
    545 }
    546 
    547 void
    548 mwrite(void)
    549 {
    550 	int n, fd;
    551 	long min, max;
    552 	n = enter("write to:", wpath, 1024, mctl, kctl, nil);
    553 	if (n <= 0) return;
    554 	if ((fd = create(wpath, OWRITE, 0666)) < 0) {
    555 		fprint(2, "%r\n");
    556 		return;
    557 	}
    558 	min = smin * Zoomout * FrameSize;
    559 	max = smax * Zoomout * FrameSize;
    560 	write(fd, pcm.p + min, max - min);
    561 	close(fd);
    562 }
    563 
    564 void
    565 mzoom(void)
    566 {
    567 	int n;
    568 	long nz;
    569 	char *s = malloc(1024);
    570 	snprint(s, 1024, "%ld", Zoomout);
    571 	n = enter("zoom", s, 1024, mctl, kctl, nil);
    572 	if (n <= 0) goto end;
    573 	nz = strtol(s, nil, 10);
    574 	if (nz > 0) {
    575 		Zoomout = nz;
    576 		clearmask();
    577 		redraw();
    578 	}
    579 end:
    580 	free(s);
    581 }
    582 
    583 void
    584 mexit(void)
    585 {
    586 	threadexitsall(nil);
    587 }
    588 
    589 void
    590 setselect(long s, long e)
    591 {
    592 	if (s < e) smin = s, smax = e;
    593 	else smax = s, smin = e;
    594 	play.state = PStop;
    595 	play.cur = smin * FrameSize * Zoomout;
    596 	redraw();
    597 }
    598 
    599 void
    600 clearcursor(void)
    601 {
    602 	int b, n, m;
    603 	Rectangle r;
    604 	Image *mask, *bg, *fg;
    605 	b = play.cur / (FrameSize * Zoomout);
    606 	n = b / dwidth;
    607 	m = b % dwidth;
    608 	if ((n < scroll) || (n >= scroll + maxbars)) return;
    609 	mask = getmask(n);
    610 
    611 	n -= scroll;
    612 	r.min.y = rbars.min.y + n * (DHeight + Margin);
    613 	r.max.y = r.min.y + DHeight;
    614 	r.min.x = rbars.min.x + m;
    615 	r.max.x = r.min.x + 1;
    616 
    617 	if ((b < smin) || (b >= smax)) {
    618 		bg = Itrfg;
    619 		fg = Itrbg;
    620 	} else {
    621 		bg = Itrbg;
    622 		fg = Itrfg;
    623 	}
    624 	lockdisplay(display);
    625 	draw(screen, r, bg, 0, ZP);
    626 	draw(screen, r, fg, mask, Pt(m, 0));
    627 	unlockdisplay(display);
    628 	needflush = 1;
    629 }
    630 
    631 void
    632 drawcursor(void)
    633 {
    634 	int b, n, m;
    635 	Rectangle r;
    636 	b = play.cur / (FrameSize * Zoomout);
    637 	n = b / dwidth;
    638 	m = b % dwidth;
    639 	if ((n < scroll) || (n >= scroll + maxbars)) return;
    640 	n -= scroll;
    641 	r.min.y = rbars.min.y + n * (DHeight + Margin);
    642 	r.max.y = r.min.y + DHeight;
    643 	r.min.x = rbars.min.x + m;
    644 	r.max.x = r.min.x + 1;
    645 
    646 
    647 	lockdisplay(display);
    648 	draw(screen, r, Icur, 0, ZP);
    649 	unlockdisplay(display);
    650 	needflush = 1;
    651 }
    652 
    653 void
    654 clearmask(void)
    655 {
    656 	int i;
    657 	for (i = 0; i < MaskCacheSize; i++) {
    658 		mask.id[i] = -1;
    659 		if (mask.img[i] != nil) {
    660 			freeimage(mask.img[i]);
    661 			mask.img[i] = nil;
    662 		}
    663 	}
    664 }
    665 
    666 long
    667 decoord(Point xy)
    668 {
    669 	long p;
    670 	p = ((scroll + row(xy.y)) * dwidth + xy.x - rbars.min.x) * Zoomout * FrameSize;
    671 	if (p < 0) p = 0;
    672 	if (p > pcm.len) {
    673 		p = pcm.len;
    674 	}
    675 	return p;
    676 }