stew

a monorepo of some sort
git clone git://git.nsmpr.xyz/stew.git
Log | Files | Refs

inst.c (9859B)


      1 /*
      2 	visual instrument editor
      3 	loads/modifies/saves envelopes
      4 	allows previewing settings by playing notes
      5 
      6 	LINE:
      7 	cmd param       loop
      8 	[b] [2b]  X 5 + [b]
      9 	
     10 	or
     11 
     12 	cmd  param
     13 	[2b] [2b]  X 4
     14 
     15 	PROG:
     16 	128 lines
     17 
     18 	cmd selects modifier and if value is absolute or relative
     19 		might also use for control commands?
     20 		modifiers: volume, frequency, wavetable, panoram
     21 		might also use extra mods for filter?
     22 	param is s16int
     23 	loop - negative values are jump
     24 
     25 	TOTAL:
     26 	2048 bytes per instrument's program, nice
     27 */
     28 
     29 #include <u.h>
     30 #include <libc.h>
     31 #include <ctype.h>
     32 #include <thread.h>
     33 #include <draw.h>
     34 #include <mouse.h>
     35 #include <cursor.h>
     36 #include <keyboard.h>
     37 
     38 #include "wavetable.h"
     39 #include "prog.h"
     40 #include "util.h"
     41 
     42 enum {
     43 	NameBufSize = 256,
     44 	OutBufSize = 882,
     45 	KbdBufSize = 64,
     46 };
     47 
     48 typedef struct FBuf FBuf;
     49 typedef struct ProgEd ProgEd;
     50 typedef struct Widget Widget;
     51 typedef struct UI UI;
     52 
     53 struct FBuf {
     54 	char path[NameBufSize];
     55 	long n;
     56 	char *p;
     57 };
     58 
     59 struct ProgEd {
     60 	char *p;
     61 	int curl, curc;
     62 };
     63 
     64 struct UI {
     65 	Mousectl *mctl;
     66 	Mouse mv;
     67 	int rv[2];
     68 	Keyboardctl *kctl;
     69 	Rune kv;
     70 	int env_n;
     71 	int env_node;
     72 	Widget *hot;
     73 	Widget **wp;
     74 };
     75 
     76 struct Widget {
     77 	Rectangle r;
     78 	Image **palette;
     79 	void *aux;
     80 	void (*draw)(UI *, void *);
     81 	void (*activate)(UI *, void *);
     82 };
     83 
     84 void usage(void);
     85 void threadsynth(void *);
     86 void threadkbd(void *);
     87 void handlemouse(UI *);
     88 void handlekeyboard(UI *);
     89 int loadfile(char *, void *, long);
     90 void rcalc(UI *);
     91 Widget * findhot(UI *);
     92 void drawwidgets(UI *);
     93 void drawprogmask(void);
     94 void wxfiledraw(UI *, void *);
     95 void wxfileactivate(UI *, void *);
     96 void wxprogdraw(UI *, void *);
     97 
     98 const Rectangle ZR = {0, 0, 1, 1};
     99 
    100 FBuf wavetable, prog;
    101 UI ui;
    102 ProgEd ped;
    103 Widget wxf_wt, wxf_prog, wx_prog;
    104 Image *progmask, *palette[8];
    105 Channel *synthc;
    106 int cw, ch; /* character width and height */
    107 
    108 void
    109 threadmain(int argc, char **argv)
    110 {
    111 
    112 	ARGBEGIN{
    113 	default:
    114 		usage();
    115 	} ARGEND
    116 	if (argc != 2) usage();
    117 
    118 	wavetable.n = sizeof(s16int) * WFSize * WTSize;
    119 	wavetable.p = mallocz(wavetable.n, 1);
    120 	if (loadfile(argv[0], wavetable.p, wavetable.n) != 0) {
    121 		fprint(2, "loading %s\n", argv[0]);
    122 		snprint(wavetable.path, NameBufSize, "%s", argv[0]);
    123 	} else wavetable.path[0] = '\0';
    124 
    125 	prog.n = ProgMax * 16;
    126 	prog.p = mallocz(prog.n, 1);
    127 	if (loadfile(argv[1], prog.p, prog.n) != 0) {
    128 		fprint(2, "loading %s\n", argv[1]);
    129 		snprint(prog.path, NameBufSize, "%s", argv[1]);
    130 	} else prog.path[0] = '\0';
    131 	ped.p = prog.p;
    132 
    133 	if (initdraw(nil, nil, "inst") == 0) sysfatal("%r");
    134 	if ((ui.mctl = initmouse(nil, screen)) == nil) sysfatal("%r");
    135 	if ((ui.kctl = initkeyboard(nil)) == nil) sysfatal("%r");
    136 	synthc = chancreate(sizeof(Rune), 8);
    137 	proccreate(threadsynth, nil, 64 * 1024);
    138 	// proccreate(threadkbd,   nil, 64 * 1024);
    139 
    140 	palette[0] = allocimage(display, ZR, XRGB32, 1, 0x7f0000ff);
    141 	palette[1] = allocimage(display, ZR, XRGB32, 1, 0xffff00ff);
    142 	palette[2] = allocimage(display, ZR, XRGB32, 1, 0xff0000ff);
    143 	palette[3] = allocimage(display, ZR, XRGB32, 1, 0x1f0000ff);
    144 
    145 	const char barsample[] = "00|00 ff|01 ff|02 ff|03 ff|04 ff|05 ff|06 ff|07 ff";
    146 	cw = stringwidth(font, "0");
    147 	ch = font->height;
    148 	
    149 	progmask = allocimage(display,
    150 	  Rect(0, 0, strlen(barsample) * cw, 256 * ch), GREY1, 0, DBlack);
    151 
    152 	drawprogmask();
    153 
    154 	wxf_wt = (Widget) {
    155 		ZR, palette, &wavetable, wxfiledraw, wxfileactivate,
    156 	};
    157 	wxf_prog = (Widget) {
    158 		ZR, palette, &prog, wxfiledraw, wxfileactivate,
    159 	};
    160 	wx_prog = (Widget) {
    161 		ZR, palette, &ped, wxprogdraw, nil,
    162 	};
    163 
    164 	Widget *wp[] = {&wxf_wt, &wxf_prog, &wx_prog, nil};
    165 	ui.wp = wp;
    166 
    167 	rcalc(&ui);
    168 
    169 	draw(screen, screen->r, palette[3], nil, ZP);
    170 	drawwidgets(&ui);
    171 	flushimage(display, 1);
    172 
    173 	Alt alts[] = {
    174 		{ui.mctl->resizec,  ui.rv, CHANRCV},
    175 		{ui.mctl->c,       &(ui.mv), CHANRCV},
    176 		{ui.kctl->c,       &(ui.kv), CHANRCV},
    177 		{nil, nil, CHANEND},
    178 	};
    179 
    180 	for (;;) switch (alt(alts)) {
    181 		case 0: /* resize   */
    182 			if(getwindow(display, Refnone) < 0) sysfatal("resize failed: %r");
    183 			draw(screen, screen->r, palette[3], nil, ZP);
    184 			rcalc(&ui);
    185 			drawwidgets(&ui);
    186 			flushimage(display, 1);
    187 			break;
    188 		case 1: /* mouse    */
    189 			handlemouse(&ui);
    190 			break;
    191 		case 2: /* keyboard */
    192 			handlekeyboard(&ui);
    193 			break;
    194 	}
    195 }
    196 
    197 void
    198 usage(void)
    199 {
    200 	fprint(2, "usage: %s wavetable prog\n", argv0);
    201 	threadexitsall("usage");
    202 }
    203 
    204 void
    205 threadsynth(void *)
    206 {
    207 
    208 	Phasor phasor;
    209 	Synth synth;
    210 	synth.table[0] = (s16int *)wavetable.p;
    211 	synth.flag = 0;
    212 	Channel *c = synthc;
    213 	Inter inter;
    214 	inter.progstart = prog.p;
    215 	int audiofd, note = 0, newnote, freq = 0, flag = 0;
    216 	Rune r;
    217 	s16int *buf;
    218 	audiofd = open("/dev/audio", OWRITE);
    219 	if (audiofd < 0) {
    220 		fprint(2, "threadsynth: %r\n");
    221 		return;
    222 	}
    223 	buf = mallocz(OutBufSize * sizeof(s16int), 1);
    224 	if (buf == nil) {
    225 		sysfatal("threadsynth failed to allocate buffer: %r");
    226 	}
    227 
    228 	long n = 1;
    229 	while (n > 0) {
    230 
    231 		if (nbrecv(c, &r) != 0) {
    232 			if (r == L'\0') {
    233 				flag = 0;
    234 			} else {
    235 				newnote = rune2note(r);
    236 				if ((newnote >=0) &&
    237 					((newnote != note) || (flag == 0))) {
    238 					interreset(&inter);
    239 					flag |= FHold;
    240 					note = newnote;
    241 					phasor.t = 0;
    242 					freq = note2freq(note);
    243 				}
    244 			}
    245 		}
    246 
    247 		interstep(&inter, flag);
    248 
    249 		synth.mod.wtsel = 0;
    250 		synth.mod.freq = freq;
    251 		synth.mod.phase = 0;
    252 		synth.mod.mod = inter.mod[0];
    253 		synth.mod.vol = inter.mod[1];
    254 		synth.mod.pan = 0;
    255 
    256 		int i;
    257 		for (i = 0; i < OutBufSize - 1; i += 2) {
    258 			union { u32int i; s16int s[2]; } out;
    259 			out.i = wtstep(&synth, &phasor);
    260 			buf[i] = out.s[0];
    261 			buf[i + 1] = out.s[1];
    262 		}
    263 		n = write(audiofd, buf, OutBufSize * sizeof(s16int));
    264 	}
    265 	free(buf);
    266 }
    267 
    268 void
    269 threadkbd(void *)
    270 {
    271 	int kbd;
    272 	Rune r;
    273 	long n;
    274 	char kbuf[KbdBufSize];
    275 	Rune rbuf[KbdBufSize];
    276 	kbd = open("/dev/kbd", OREAD);
    277 	if (kbd < 0) {
    278 		fprint(2, "threadkbd: %r\n");
    279 		return;
    280 	}
    281 	memset(kbuf, 0, KbdBufSize);
    282 
    283 	while ((read(kbd, kbuf, KbdBufSize)) > 0) {
    284 
    285 
    286 		n = runesnprint(rbuf, KbdBufSize, "%s", kbuf + 1);
    287 
    288 		if (rbuf[n-1] == Kdel) break;
    289 
    290 		switch (kbuf[0]) {
    291 		case 'K':
    292 		case 'k':
    293 			if (n == 0) r = L'\0';
    294 			else r = rbuf[n - 1];
    295 			nbsend(synthc, &r);
    296 			break;
    297 		case 'c':
    298 			/* hacks! */
    299 			// nbsend(ui.kctl->c, &rbuf[0]);
    300 			break;
    301 		}
    302 		memset(kbuf, 0, KbdBufSize);
    303 	}
    304 	close(kbd);
    305 }
    306 
    307 void
    308 handlemouse(UI *ui)
    309 {
    310 	
    311 	enum { MFree, };
    312 	static state = MFree;
    313 	switch (state) {
    314 	case MFree:
    315 		ui->hot = findhot(ui);
    316 		if ((ui->mv.buttons == 1) && (ui->hot != nil)) {
    317 			if (ui->hot->activate) ui->hot->activate(ui, ui->hot);
    318 			drawwidgets(ui);
    319 			flushimage(display, 1);
    320 		}
    321 		if (ui->mv.buttons == 4) threadexitsall(nil);
    322 		break;
    323 	}
    324 }
    325 
    326 void
    327 handlekeyboard(UI *ui)
    328 {
    329 	char buf[256];
    330 	switch (ui->kv) {
    331 	case ' ':
    332 		proccreate(threadkbd, nil, 64 * 1024);
    333 		break;
    334 	case Kup:
    335 		ped.curl--;
    336 		if (ped.curl < 0) ped.curl = 127;
    337 		break;
    338 	case Kdown:
    339 		ped.curl++;
    340 		if (ped.curl >= 128) ped.curl = 0;
    341 		break;
    342 	case Kleft:
    343 		ped.curc--;
    344 		if (ped.curc < 0) ped.curc = 31;
    345 		break;
    346 	case Kright:
    347 		ped.curc++;
    348 		if (ped.curc >= 32) ped.curc = 0;
    349 		break;
    350 	default:
    351 		if (isxdigit(ui->kv)) {
    352 			sprint(buf, "%C\0", ui->kv);
    353 			int n, i;
    354 			i = ped.curl * 32 + ped.curc;
    355 			n = strtol(buf, nil, 16);
    356 			if ((i & 1) != 0) {
    357 				ped.p[i>>1] = ped.p[i>>1] & 0xf0 | n;
    358 			} else {
    359 				ped.p[i>>1] = ped.p[i>>1] & 0x0f | (n << 4);
    360 			}
    361 			drawprogmask();
    362 			if ((ped.curc & 1) == 0) ped.curc++;
    363 			else {
    364 				ped.curc--;
    365 				ped.curl = (ped.curl + 1) % ProgMax;
    366 			}
    367 		}
    368 	}
    369 	drawwidgets(ui);
    370 	flushimage(display, 1);
    371 }
    372 
    373 int
    374 loadfile(char *path, void *p, long size)
    375 {
    376 	int fd;
    377 	long n;
    378 	fd = open(path, OREAD);
    379 	if (fd < 0) {
    380 		fprint(2, "%r\n");
    381 		return 0;
    382 	};
    383 	n = read(fd, p, size);
    384 	close(fd);
    385 	return n > 0;
    386 }
    387 
    388 int
    389 loadwavetable(void *p, char *f)
    390 {
    391 	return loadfile(f, p, sizeof(s16int) * WFSize * WTSize);
    392 }
    393 
    394 void
    395 rcalc(UI *ui)
    396 {
    397 	Point min = screen->r.min;
    398 	Point max = screen->r.max;
    399 
    400 	Rectangle r1 = Rpt(min, Pt(max.x, min.y + ch));
    401 	ui->wp[0]->r = r1;
    402 	Rectangle r2 = Rpt(Pt(min.x, r1.max.y), Pt(max.x, r1.max.y + ch));
    403 	ui->wp[1]->r = r2;
    404 	Rectangle r3 = Rpt(Pt(min.x, r2.max.y), max);
    405 	ui->wp[2]->r = r3;
    406 	
    407 }
    408 
    409 Widget *
    410 findhot(UI *ui)
    411 {
    412 	Widget **w = ui->wp;
    413 	Point xy = ui->mv.xy;
    414 	for (; *w != nil; w++) {
    415 		if (ptinrect(xy, (*w)->r) != 0) return *w;
    416 	}
    417 	return nil;
    418 }
    419 
    420 void
    421 drawwidgets(UI *ui)
    422 {
    423 	Widget **wp = ui->wp;
    424 	while (*wp != nil) {
    425 		if ((*wp)->draw) (*wp)->draw(ui, *wp);
    426 		wp++;
    427 	};
    428 }
    429 
    430 void
    431 drawprogmask(void)
    432 {
    433 	int i;
    434 	draw(progmask, progmask->r, display->black, nil, ZP);
    435 	for (i = 0; i < 128; i++) {
    436 		Point pt = Pt(0, i * ch);
    437 		char buf[256];
    438 		char *prp = prog.p + i * 16;
    439 		snprint(buf, 256,
    440 		  "%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX\0",
    441 		  i,
    442 		  prp[0], prp[1], prp[2], prp[3], prp[4], prp[5], prp[6], prp[7], 
    443 		  prp[8], prp[9], prp[10], prp[11], prp[12], prp[13], prp[14], prp[15]);
    444 		
    445 		string(progmask, pt, display->white, ZP, font, buf);
    446 	}
    447 }
    448 
    449 void
    450 wxfiledraw(UI *, void *arg)
    451 {
    452 	Widget *w = (Widget *) arg;
    453 	FBuf *fb = w->aux;
    454 	static char buf[NameBufSize];
    455 	snprint(buf, NameBufSize, "path: %s", fb->path);
    456 	draw(screen, w->r, w->palette[0], nil, ZP);
    457 	string(screen, w->r.min, w->palette[1], ZP, font, buf);
    458 }
    459 
    460 void
    461 wxfileactivate(UI *ui, void *arg)
    462 {
    463 	Widget *w = (Widget *) arg;
    464 	FBuf *fb = w->aux;
    465 	if (enter("path:", fb->path, NameBufSize, ui->mctl, ui->kctl, nil) != 0) {
    466 		loadfile(fb->path, fb->p, fb->n);
    467 	}
    468 	drawprogmask();
    469 }
    470 
    471 void
    472 wxprogdraw(UI *, void *arg)
    473 {
    474 	int n;
    475 	Rectangle r;
    476 	Widget *w = (Widget *) arg;
    477 	ProgEd *ped = w->aux;
    478 
    479 	draw(screen, w->r, w->palette[3], nil, ZP);
    480 
    481 	r = w->r;
    482 	r.min.y += ped->curl * ch;
    483 	r.max.y = r.min.y + ch;
    484 	draw(screen, r, w->palette[0], nil, ZP);
    485 
    486 
    487 	draw(screen, w->r, w->palette[2], progmask, ZP);
    488 
    489 	n = ped->curc + 3 + (ped->curc)/2;
    490 
    491 	r = w->r;
    492 	r.min.x += n * cw;
    493 	r.max.x = r.min.x + cw;
    494 	r.min.y += ped->curl * ch;
    495 	r.max.y = r.min.y + ch;
    496 	draw(screen, r, w->palette[1], progmask, subpt(r.min, w->r.min));
    497 }