shithub: etoys

ref: b3b41bdae9762fe39ebe567d15cbd9e484e83c9d
dir: /beatlet.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <geometry.h>

enum {
	NSAMP	= 1323,		/* 30ms */
};

static int debug;

static Point2 ZP2 = {0,0,1};
RFrame beatrf;
Image *screenb;
Channel *drawc;

Point2
p2(Point p)
{
	return Pt2(p.x, p.y, 1);
}

Point
p2p(Point2 p)
{
	return Pt(p.x, p.y);
}

Point
toscreen(Point2 p)
{
	return p2p(invrframexform(p, beatrf));
}

void
redraw(void)
{
	lockdisplay(display);
	draw(screen, screen->r, screenb, nil, screenb->r.min);
	flushimage(display, 1);
	unlockdisplay(display);
}

void
drawproc(void *arg)
{
	Channel *c;

	threadsetname("drawproc");

	c = arg;

	for(;;){
		recv(c, nil);
		redraw();
	}
}

void
beatproc(void *arg)
{
	Point2 v;
	Point poly[NSAMP];
	u32int ss[NSAMP];	/* stereo samples */
	s32int as[NSAMP], s;	/* avg'd samples */
	u16int min, max;
	int fd, i, ns;
	double θ;
	uvlong frame, t0, t1;
	char buf[32];

	threadsetname("beatproc");

	fd = *(int*)arg;
	ns = frame = 0;

	t0 = nsec();
	while(readn(fd, ss, 2*2*NSAMP) > 0){
		min = (1<<15) - 1;
		max = 0;
		for(i = 0; i < nelem(ss); i++){
			s = (s16int)(ss[i] & 0xFFFF) + (s16int)(ss[i]>>16 & 0xFFFF);
			/* [-2¹⁶, 2¹⁶) → [0, 2¹⁵) */
			s = (s/2 + (1<<15))/2;
			min = s < min? s: min;
			max = s > max? s: max;
			as[i] = s;
		}
		lockdisplay(display);
		for(i = 0; i < nelem(as); i++){
			θ = 2*PI * ((double)(ns++ % NSAMP)/NSAMP);
			v = Vec2(cos(θ), sin(θ));
			s = as[i];
			poly[i] = toscreen(addpt2(ZP2, mulpt2(v, 4*(s-min)*100/max)));
//			line(screenb, toscreen(ZP2), toscreen(addpt2(ZP2, mulpt2(v, 400))), 0, 0, 0, display->black, ZP);
//			line(screenb, toscreen(ZP2), toscreen(addpt2(ZP2, mulpt2(v, 4*(s-min)*100/max))), 0, 0, 0, display->white, ZP);
		}
		draw(screenb, screenb->r, display->black, nil, ZP);
		fillpoly(screenb, poly, nelem(poly), 1, display->white, ZP);

		if(debug){
			t1 = nsec();
			snprint(buf, sizeof(buf), "%llud (%3.0fHz)", ++frame, 1e9/(t1-t0));
			t0 = t1;
			stringbg(screenb, addpt(screen->r.min, Pt(20, 20)), display->white, ZP, font, buf, display->black, ZP);
		}
		unlockdisplay(display);
		nbsend(drawc, nil);
	}
}

void
key(Rune r)
{
	switch(r){
	case Kdel:
	case 'q':
		threadexitsall(nil);
	}
}

void
resized(void)
{
	lockdisplay(display);
	if(getwindow(display, Refnone) < 0)
		sysfatal("resize failed");
	freeimage(screenb);
	screenb = allocimage(display, screen->r, screen->chan, 0, DBlack);
	beatrf.p = p2(divpt(addpt(screen->r.min, screen->r.max), 2));
	unlockdisplay(display);
	nbsend(drawc, nil);
}

void
usage(void)
{
	fprint(2, "usage: %s /dev/audio\n", argv0);
	exits("usage");
}

void
threadmain(int argc, char *argv[])
{
	Mousectl *mc;
	Keyboardctl *kc;
	Rune r;
	int fd;

	ARGBEGIN{
	case 'd': debug++; break;
	default: usage();
	}ARGEND;
	if(argc != 1)
		usage();

	fd = open(argv[0], OREAD);
	if(fd < 0)
		sysfatal("open: %r");

	if(initdraw(nil, nil, nil) < 0)
		sysfatal("initdraw: %r");
	if((mc = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if((kc = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");

	beatrf.p = p2(divpt(addpt(screen->r.min, screen->r.max), 2));
	beatrf.bx = Vec2(1, 0);
	beatrf.by = Vec2(0,-1);
	screenb = allocimage(display, screen->r, screen->chan, 0, DBlack);
	if(screenb == nil)
		sysfatal("allocimage: %r");

	drawc = chancreate(sizeof(void*), 1);
	proccreate(drawproc, drawc, 8*1024);
	proccreate(beatproc, &fd, 32*1024);

	display->locking = 1;
	unlockdisplay(display);
	nbsend(drawc, nil);

	for(;;){
		enum { MOUSE, RESIZE, KEYBOARD };
		Alt a[] = {
			{mc->c, &mc->Mouse, CHANRCV},
			{mc->resizec, nil, CHANRCV},
			{kc->c, &r, CHANRCV},
			{nil, nil, CHANEND}
		};

		switch(alt(a)){
		case RESIZE:
			resized();
			break;
		case KEYBOARD:
			key(r);
			break;
		}
	}
}