💾 Archived View for runjimmyrunrunyoufuckerrun.com › src › piano.c captured on 2021-12-17 at 13:26:06.

View Raw

More Information

-=-=-=-=-=-=-

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <thread.h>

enum{
	SAW = 0, TRI, SIN, SQ, WAVES,
	WAV = 128,
	RATE = 44100,
	BUF = RATE * 4 / 50,
	OCTS = 9,
	NOTES = OCTS * 12,
	SHARPS = 1354,	/* 0b010101001010 */
	WWD = 48,
	WHT = 180,
	BWD = 28,
	BHT = 120,
};

char *waveforms[] = {"sawtooth", "triangle", "sine", "square", nil};
Menu b2 = {waveforms, nil, 0};
char *name = "CCDDEFFGGAAB";
char *key = "\t1q2we4r5t6yu8i9op-[=]\b\\";
int oct = 4, vol = 63, wav[WAVES][WAV], wavn;
char on[NOTES];
u16int θ[NOTES], Δ[NOTES];
Rectangle w[14], b[10];
int woff[14] = {0,2,4,5,7,9,11,12,14,16,17,19,21,23};
int boff[10] = {1,3,6,8,10,13,15,18,20,22};
Image *down;
Rune info[128];
Point infop;

void
redraw(void)
{
	int i;
	Rune *r;

	draw(screen, screen->r, down, nil, ZP);
	for(i = 0; i < nelem(w); i++){
		draw(screen, w[i], on[12*oct+woff[i]]?down:display->white, nil, ZP);
		border(screen, w[i], 1, display->black, ZP);
	}
	for(i = 0; i < nelem(b); i++){
		draw(screen, b[i], on[12*oct+boff[i]]?down:display->black, nil, ZP);
		border(screen, b[i], 1, display->black, ZP);
	}
	for(i = 0, r = info; i < oct; i++)
		*r++ = '-';
	*r++ = '0' + i++;
	*r++ = '0' + i;
	while(++i < OCTS)
		*r++ = '-';
	*r++ = ' ';
	for(i = 0; i < NOTES; i++)
		if(on[i]){
			*r++ = name[i % 12];
			if(SHARPS & 1 << i % 12)
				*r++ = 0x266e;	/* â™® */
			*r++ = i / 12 + '0';
			*r++ = ' ';
			if(r - info > nelem(info) - 5)
				break;
		}
	*r = '\0';
	runestring(screen, infop, display->black, ZP, font, info);
	flushimage(display, 1);
}

void
rthread(void *arg)
{
	Mousectl *m;
	Point p;
	int fd, i;

	for(m = arg;;){
		recvul(m->resizec);
		if(getwindow(display, Refnone) < 0)
			sysfatal("getwindow: %r");
		if((Dx(screen->r) != nelem(w) * WWD
		 || Dy(screen->r) != WHT + font->height)
		&&(fd = open("/dev/wctl", OWRITE)) >= 0){
			fprint(fd, "resize -dx %d -dy %d",
				nelem(w) * WWD + 2 * Borderwidth,
				WHT + font->height + 2 * Borderwidth);
			close(fd);
		}
		for(i = 0, p = screen->r.min; i < nelem(w); i++, p.x += WWD)
			w[i] = rectaddpt(Rect(0, 0, WWD, WHT), p);
		for(i = nelem(b), p.x -= WWD + BWD / 2; i--; p.x -= WWD){
			b[i] = rectaddpt(Rect(0, 0, BWD, BHT), p);
			if(i % 5 == 0 || i % 5 == 2)
				p.x -= WWD;
		}
		infop = Pt(screen->r.min.x, screen->r.max.y - font->height);
		redraw();
	}
}

void
mthread(void *arg)
{
	Mousectl *m;
	int i;

	for(m = arg;;){
		recv(m->c, m);
		switch(m->buttons){
		default:
			continue;
		case 1:
			for(i = 0; i < nelem(b); i++)
				if(ptinrect(m->xy, b[i])){
					on[oct*12+boff[i]] |= 2;
					goto draw;
				}
			for(i = 0; i < nelem(w); i++)
				if(ptinrect(m->xy, w[i])){
					on[oct*12+woff[i]] |= 2;
					break;
				}
			break;
		case 2:
			i = menuhit(2, m, &b2, nil);
			if(i >= 0 && i < WAVES)
				wavn = i;
			break;
		case 4:
			for(i = 0; i < nelem(b); i++)
				if(ptinrect(m->xy, b[i])){
					on[oct*12+boff[i]] &= 1;
					goto draw;
				}
			for(i = 0; i < nelem(w); i++)
				if(ptinrect(m->xy, w[i])){
					on[oct*12+woff[i]] &= 1;
					break;
				}
			break;
		}
draw:		redraw();
	}
}

void
kproc(void *)
{
	int fd, n, i;
	char buf[128], *p;

	if((fd = open("/dev/kbd", OREAD)) < 0)
		sysfatal("open: %r");
	for(;;){
		if((n = read(fd, buf, sizeof(buf) - 1)) < 0)
			sysfatal("read: %r");
		buf[n] = '\0';
		for(p = buf; p - buf < n;){
			if(*p++ != 'c'){
				if(strchr(p, Kdel)){
					close(fd);
					threadexitsall(nil);
				}
				if(strchr(p, 'a') && oct < OCTS - 2)
					oct++;
				if(strchr(p, 'z') && oct)
					oct--;
				if(strchr(p, 's') && vol < 126)
					vol += 7;
				if(strchr(p, 'x') && vol > 6)
					vol -= 7;
				for(i = 0; i < strlen(key); i++){
					if(strchr(p, key[i]))
						on[12 * oct + i] |= 1;
					else
						on[12 * oct + i] &= 2;
				}
				redraw();
			}
			while(*p++)
				;
		}
	}
}

void
threadmain(int,char *argv[])
{
	Mousectl *m;
	int fd, i, s;
	uchar buf[BUF], *p;

	for(i = 0; i < WAV / 2; i++){
		wav[SAW][i] = 64 - 2 * 64 * i / WAV;
		wav[TRI][i] = 64 - 64 * i * 4 / WAV;
		wav[SIN][i] = 64 * sin(2 * PI * i / WAV);
		wav[SQ][i] = -64;
	}
	for(; i < WAV; i++){
		wav[SAW][i] = 64 - 2 * 64 * i / WAV;
		wav[TRI][i] = 64 * (i - WAV / 2) * 4 / WAV - 64;
		wav[SIN][i] = 64 * sin(2 * PI * i / WAV);
		wav[SQ][i] = 64;
	}
	for(i = 0; i < NOTES; i++)
		Δ[i] = (440 << 16) * pow(2, (i - 57) / 12.0) / RATE;
	if((fd = open("/dev/audio", OWRITE)) < 0)
		sysfatal("open: %r");
	if(initdraw(nil, nil, argv0 = argv[0]) < 0)
		sysfatal("initdraw: %r");
	if((down = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen)) == nil)
		sysfatal("allocimage: %r");
	if((m = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if(threadcreate(rthread, m, mainstacksize) < 0)
		sysfatal("threadcreate: %r");
	if(threadcreate(mthread, m, mainstacksize) < 0)
		sysfatal("threadcreate: %r");
	if(proccreate(kproc, nil, mainstacksize) < 0)
		sysfatal("proccreate: %r");
	sendul(m->resizec, 1);
	for(;;){
		for(p = buf; p - buf < BUF; p += 4){
			for(i = 0, s = 0; i < NOTES; i++)
				if(on[i])
					s += wav[wavn][(θ[i] += Δ[i]) >> 9];
			s *= vol;
			p[0] = p[2] = s;
			p[1] = p[3] = s >> 8;
		}
		if(write(fd, buf, BUF) != BUF)
			break;
		yield();
	}
	close(fd);
}