shithub: orca

ref: b2bacf2993e5aef1de9edaa2e6f271cbb6030ad2
dir: /plan9.c/

View raw version
#include "plan9.h"
#include "field.h"
#include "gbuffer.h"
#include "sim.h"
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <thread.h>

#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))

enum {
	Txtoff = 16,
};

static struct {
	u8int u[4];
	Usz at;
}noteoff[16*128]; /* 16 channels, 128 notes each */

static Rune *linebuf;
static Usz tick;
static int gridw = 8, gridh = 8;
static int bpm = 120, insert = 1;
static int curx, cury;
static Image *curbg;
static int charw, charh;
static Field field;
static Mbuf_reusable mbuf;
static Oevent_list events;

static char *menu3i[] = {
	"load",
	"save",
	nil,
};

static Menu menu3 = {
	.item = menu3i,
};

Usz
orca_round_up_power2(Usz x)
{
	x -= 1;
	x |= x >> 1;
	x |= x >> 2;
	x |= x >> 4;
	x |= x >> 8;
	x |= x >> 16;
	return x + 1;
}

bool
orca_is_valid_glyph(Glyph c)
{
	if (c >= '0' && c <= '9')
		return true;
	if (c >= 'A' && c <= 'Z')
		return true;
	if (c >= 'a' && c <= 'z')
		return true;
	switch (c) {
	case '!':
	case '#':
	case '%':
	case '*':
	case '.':
	case ':':
	case ';':
	case '=':
	case '?':
		return true;
	}
	return false;
}

static void
process(Oevent_list *events)
{
	int i, off;
	Oevent *e;
	u8int u[4];

	for (e = events->buffer, i = 0; i < events->count; i++, e++) {
		if (e->any.oevent_type == Oevent_type_midi_note) {
			Oevent_midi_note const *n = &e->midi_note;
			u[0] = 1;
			u[1] = 0x90 | n->channel;
			u[2] = (n->octave + 1)*12 + n->note;
			u[3] = n->velocity;
			write(1, u, 4);

			off = n->channel*128 + u[2];
			noteoff[off].u[1] = 0x80 | n->channel;
			noteoff[off].u[2] = u[2];
			noteoff[off].u[3] = 0;
			noteoff[off].at = tick + n->duration;
		}
	}

	sleep(150);

	for (i = 0; i < nelem(noteoff); i++) {
		if (noteoff[i].at > 0 && noteoff[i].at < tick) {
			write(1, noteoff[i].u, 4);
			noteoff[i].at = 0;
		}
	}
}

static void
orcathread(void *)
{
	int w, h;

	w = field.width;
	h = field.height;
	mbuffer_clear(mbuf.buffer, h, w);
	oevent_list_clear(&events);
	orca_run(field.buffer, mbuf.buffer, h, w, tick, &events, 0);
	process(&events);
}

static void
redraw(void)
{
	Point p, top;
	Rune cursor;
	int x, y, len;

	draw(screen, screen->r, display->black, nil, ZP);
	p = screen->r.min;
	p.x += Txtoff;
	p.y += Txtoff;
	top = p;
	for (y = 0; y < field.height; y++) {
		for (x = 0; x < field.width; x++) {
			Rune c = field.buffer[field.width*y + x];
			if (c == L'.')
				c = L'·';
			linebuf[x] = c;
			if (y == cury && x == curx)
				cursor = c;
		}
		linebuf[x] = 0;
		runestring(screen, p, display->white, ZP, font, linebuf);
		p.y += font->height;
	}

	p.y += 2 * font->height;

	/* field size */
	p.x = screen->r.min.x + Txtoff;
	len = runesprint(linebuf, "%udx%ud", field.width, field.height);
	runestring(screen, p, display->white, ZP, font, linebuf);

	/* cursor position */
	p.y += font->height;
	runesprint(linebuf, "%ud,%ud", curx, cury);
	runestring(screen, p, display->white, ZP, font, linebuf);

	/* grid size */
	p.y -= font->height;
	p.x += charw * (len + 3);
	len = runesprint(linebuf, "%d/%d", gridw, gridh);
	runestring(screen, p, display->white, ZP, font, linebuf);

	/* ticks */
	p.x += charw * (len + 3);
	runesprint(linebuf, "%ludf", tick);
	runestring(screen, p, display->white, ZP, font, linebuf);

	/* insert/append mode */
	p.y += font->height;
	len = runesprint(linebuf, "%s", insert ? "insert" : "append");
	runestring(screen, p, display->white, ZP, font, linebuf);

	/* bpm */
	p.y -= font->height;
	p.x += charw * (len + 3);
	runesprint(linebuf, "%d", bpm);
	runestring(screen, p, display->white, ZP, font, linebuf);

	/* cursor bg */
	p = top;
	p.x += curx*stringwidth(font, " ");
	p.y += cury*font->height;
	runestringnbgop(screen, p, display->black, ZP, font, &cursor, 1, curbg, ZP, SoverD);

	flushimage(display, 1);
}

static int
fieldload(char *path)
{
	Field_load_error e;

	if ((e = field_load_file(path, &field)) != Field_load_error_ok) {
		werrstr(field_load_error_string(e));
		return -1;
	}

	return 0;
}

static void
fieldset(Rune key)
{
	field.buffer[curx + field.width*cury] = key;
	if (!insert && curx < field.width-1)
		curx++;
}

static void
screensize(int *w, int *h)
{
	*w = (Dx(screen->r) - 2*Txtoff) / charw;
	*h = ((Dy(screen->r) - 2*Txtoff) - 3*charh) / charh;
}

static void
select(void)
{
}

void
threadmain(int argc, char **argv)
{
	Mousectl *mctl;
	Keyboardctl *kctl;
	Field copyfield;
	Rune key;
	Mouse m;
	char tmp[256];
	int oldw, oldh, w, h;
	Alt a[] = {
		{ nil, &m, CHANRCV },
		{ nil, nil, CHANRCV },
		{ nil, &key, CHANRCV },
		{ nil, nil,  CHANNOBLK},
	};

	USED(argc, argv);

	srand(time(0));
	threadsetname("orca");

	if(initdraw(nil, nil, "orca") < 0)
		sysfatal("initdraw: %r");
	if ((mctl = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if ((kctl = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");

	a[0].c = mctl->c;
	a[1].c = mctl->resizec;
	a[2].c = kctl->c;

	curbg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DYellow);
	charw = stringwidth(font, "X");
	charh = font->height;

	screensize(&w, &h);
	field_init_fill(&field, h, w, '.');
	field_init(&copyfield);

	linebuf = malloc(sizeof(Rune)*MAX(w+1, 64));
	memset(noteoff, 0, sizeof(noteoff));
	mbuf_reusable_init(&mbuf);
	mbuf_reusable_ensure_size(&mbuf, h, w);
	oevent_list_init(&events);

	for (tick = 0;; tick++) {
		orcathread(nil);
		redraw();

		oldw = w = field.width;
		oldh = h = field.height;

		switch (alt(a)) {
		case 0: /* mouse */
			switch (m.buttons & 7) {
			case 1:
				select();
				break;
			case 2:
				break;
			case 4:
				switch (menuhit(3, mctl, &menu3, nil)) {
				case 0:
					tmp[0] = 0;
					if (enter("load file:", tmp, sizeof(tmp), mctl, kctl, nil) > 0) {
						if (fieldload(tmp) == 0) {
							w = field.width;
							h = field.height;
						} else {
							/* FIXME display the error */
						}
					}
					break;
				}
				break;
			}
			break;

		case 1: /* resize */
			getwindow(display, Refnone);
			break;

		case 2: /* keyboard */
			switch (key) {
			case 0x0b: /* C-k */
			case Kup:
				cury = MAX(0, cury-1);
				break;
			case '\n': /* C-j */
			case Kdown:
				cury = MIN(h-1, cury+1);
				break;
			case Kbs: /* C-h */
			case Kleft:
				curx = MAX(0, curx-1);
				break;
			case 0x0c: /* C-l */
			case Kright:
				curx = MIN(w-1, curx+1);
				break;
			case 0x11: /* C-q */
				goto end;
			case 0x12: /* C-r */
				tick = -1;
				break;
			case '(':
				w = MAX(1, w-gridw);
				break;
			case ')':
				w += gridw;
				break;
			case '_':
				h = MAX(1, h-gridh);
				break;
			case '+':
				h += gridh;
				break;
			case '>':
				bpm++;
				break;
			case '<':
				bpm = MAX(1, bpm-1);
				break;
			case Kins:
				insert = !insert;
				break;
			case Kesc:
				if (!insert)
					insert = 1;
				/* FIXME else remove selection */
				break;
			default:
				if (key == Kdel)
					key = '.';
				if (orca_is_valid_glyph(key))
					fieldset(key);
				else
					fprint(2, "unhandled key %04x\n", key);
				break;
			}

			if (w != oldw || h != oldh) {
				field_copy(&field, &copyfield);
				field_resize_raw(&field, h, w);
				memset(field.buffer, '.', w*h);
				gbuffer_copy_subrect(
					copyfield.buffer,
					field.buffer,
					copyfield.height, copyfield.width,
					field.height, field.width,
					0, 0, 0, 0,
					MIN(field.height, copyfield.height), MIN(field.width, copyfield.width)
				);
			}
		}

		if (w != oldw || h != oldh) {
			mbuf_reusable_ensure_size(&mbuf, h, w);
			linebuf = realloc(linebuf, sizeof(Rune)*MAX(w+1, 64));
		}
	}

end:
	mbuf_reusable_deinit(&mbuf);
	oevent_list_deinit(&events);
	field_deinit(&field);
	field_deinit(&copyfield);

	threadexitsall(nil);
}