๐Ÿ’พ Archived View for runjimmyrunrunyoufuckerrun.com โ€บ src โ€บ opldrums.c captured on 2021-12-17 at 13:26:06.

View Raw

More Information

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

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
/* drum machine / instrument editor for opl3(1) in percussion mode
 * http://www.fit.vutbr.cz/~arnost/opl/opl3.html
 * http://midibox.org/forums/topic/18625-opl3-percussion-mode-map/
 */
uchar reg[] = {
	0xBD,
	0xA6, 0xA7, 0xA8,
	0xB6, 0xB7, 0xB8,
	0xC6, 0xC7, 0xC8,
	0x30, 0x33, 0x34, 0x32, 0x35, 0x31,
	0x70, 0x73, 0x74, 0x72, 0x75, 0x71,
	0x90, 0x93, 0x94, 0x92, 0x95, 0x91,
	0xF0, 0xF3, 0xF4, 0xF2, 0xF5, 0xF1,
	0x50, 0x53, 0x54, 0x52, 0x55, 0x51
};
uchar val[] = {
	0x20,	/* global tremolo & vibrato depth, percussion mode enable */
	/* Channel Settings (BD, SD, TT) */
	0x04, 0x04, 0xb1,		/* pitch: lower bits */
	0x06, 0x16, 0x0e,		/* pitch: upper bits */
	0x30, 0x30, 0x30,		/* feedback, synth type */
	/* Operator Settings (BDmod, BDcar, SD, TT, CY, HH) */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* trem, vib, sus, mult */
	0xF0, 0xF0, 0xD0, 0xF0, 0xC0, 0xF0,	/* attack, decay */
	0x06, 0x06, 0x06, 0x05, 0x05, 0x07,	/* sustain, release */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* wave */
	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00	/* vol */
};
int tee, oplon, seqpos, seqlen = 16, bpm = 80;
Keyboardctl *kbd;
Mousectl *โŒ–;
Image *bg, *fg;
Point seqpt;
Rectangle box, seqr, cell;
uchar seq[128] = {0};
char err[ERRMAX];
char *mb3[] = {"write pattern", "write drum settings", "quit", nil};
char *toggle[] = {"off", "on", nil};
char *synth[] = {"FM Mode", "AM Mode", nil};
char *tremdepth[] = {"Reg Trem", "Deep Trem", nil};
char *vibdepth[] = {"Reg Vib", "Deep Vib", nil};
char *nibble[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", nil};
char *mult[] = {"ยฝ", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "10", "12", "12", "15", "15", nil};
char *wave[] = {
	/* OPL2 */	"Sine", "Half Sine", "Absolute Sine", "Pulse Sine",
	/*OPL3 */	"Even Sine", "Even Abs. Sine", "Square", "Derived Square", nil
};
char *feedback[] = {
	"0 Feedback", "ยนโ„โ‚โ‚† Feedback", "โ…› Feedback", "โ…™ Feedback",
	"ยฝ Feedback", "1x Feedback", "2x Feedback", "4x Feedback", nil
};
char *pitch[] = {
	"C0","C#0","D0","D#0","E0","F0","F#0","G0","G#0","A0","A#0","B0",
	"C1","C#1","D1","D#1","E1","F1","F#1","G1","G#1","A1","A#1","B1",
	"C2","C#2","D2","D#2","E2","F2","F#2","G2","G#2","A2","A#2","B2",
	"C3","C#3","D3","D#3","E3","F3","F#3","G3","G#3","A3","A#3","B3",
	"C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4",
	"C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5",
	"C6","C#6","D6","D#6","E6","F6","F#6","G6","G#6","A6","A#6","B6",
	"C7","C#7","D7","D#7","E7","F7","F#7","G7","G#7","A7","A#7","B7",
	"C8","C#8","D8","D#8","E8","F8","F#8",nil
};
char *vol[] = {
	"63","62","61","60","59","58","57","56","55","54","53","52","51","50","49","48",
	"47","46","45","44","43","42","41","40","39","38","37","36","35","34","33","32",
	"31","30","29","28","27","26","25","24","23","22","21","20","19","18","17","16",
	"15","14","13","12","11","10","9","8","7","6","5","4","3","2","1","0", nil
};
Menu b3 = {mb3, nil, 0}, m[] = {
	/* Operator Settings */
	{toggle}, {toggle}, {toggle}, {toggle}, {toggle}, {toggle},	/* Tremolo */
	{toggle}, {toggle}, {toggle}, {toggle}, {toggle}, {toggle},	/* Vibrato */
	{toggle}, {toggle}, {toggle}, {toggle}, {toggle}, {toggle},	/* Sustaining */
	{mult}, {mult}, {mult}, {mult}, {mult}, {mult},	/* Multiplier */
	{nibble}, {nibble}, {nibble}, {nibble}, {nibble}, {nibble},	/* Attack */
	{nibble}, {nibble}, {nibble}, {nibble}, {nibble}, {nibble},	/* Decay */
	{nibble}, {nibble}, {nibble}, {nibble}, {nibble}, {nibble},	/* Sustain */
	{nibble}, {nibble}, {nibble}, {nibble}, {nibble}, {nibble},	/* Release */
	{wave}, {wave}, {wave}, {wave}, {wave}, {wave},	/* Wave */
	{vol}, {vol}, {vol}, {vol}, {vol}, {vol},	/* Volume */
	/* Channel Settings, Global Settings */
	{nil}, {pitch}, {pitch}, {pitch}, {nil}, {nil},
	{synth}, {feedback}, {tremdepth}, {vibdepth}, {nil}, {nil}
};
short freq[] = {
0x158, 0x16d, 0x183, 0x19a, 0x1b2, 0x1cc, 0x1e7, 0x204, 0x223, 0x244, 0x266, 0x28b,
0x2b1, 0x2da, 0x306, 0x334, 0x365, 0x398, 0x3cf, 0x604, 0x623, 0x644, 0x666, 0x68b,
0x6b1, 0x6da, 0x706, 0x734, 0x765, 0x798, 0x7cf, 0xa04, 0xa23, 0xa44, 0xa66, 0xa8b,
0xab1, 0xada, 0xb06, 0xb34, 0xb65, 0xb98, 0xbcf, 0xe04, 0xe23, 0xe44, 0xe66, 0xe8b,
0xeb1, 0xeda, 0xf06, 0xf34, 0xf65, 0xf98, 0xfcf, 0x1204, 0x1223, 0x1244, 0x1266, 0x128b,
0x12b1, 0x12da, 0x1306, 0x1334, 0x1365, 0x1398, 0x13cf, 0x1604, 0x1623, 0x1644, 0x1666, 0x168b,
0x16b1, 0x16da, 0x1706, 0x1734, 0x1765, 0x1798, 0x17cf, 0x1a04, 0x1a23, 0x1a44, 0x1a66, 0x1a8b,
0x1ab1, 0x1ada, 0x1b06, 0x1b34, 0x1b65, 0x1b98, 0x1bcf, 0x1e04, 0x1e23, 0x1e44, 0x1e66, 0x1e8b,
0x1eb1, 0x1eda, 0x1f06, 0x1f34, 0x1f65, 0x1f98, 0x1fcf
};
void
oplproc(void*){
	vlong t, incr, zzz;
	int fd, p[2], i;
	long n;
	uchar *buf, s[nelem(reg) * 5] = {0x01, 0, 0x20};
	fd = open("/dev/audio", OWRITE);
	if(fd < 0){
		*err = 0;
		errstr(err, ERRMAX);
		enter(err, nil, 0, โŒ–, kbd, nil);
		exits(nil);
	}
	if(pipe(p) < 0){
		*err = 0;
		errstr(err, ERRMAX);
		enter(err, nil, 0, โŒ–, kbd, nil);
		exits(nil);
	}
	switch(fork()){
	case -1:
		*err = 0;
		errstr(err, ERRMAX);
		enter(err, nil, 0, โŒ–, kbd, nil);
		exits(nil);
	case 0:
		close(p[0]);
		dup(p[1], 0);
		close(p[1]);
		if(tee){
			if((buf = malloc(8192)) == nil)
				sysfatal("malloc: %r");
			if(pipe(p) < 0)
				sysfatal("pipe: %r");
			switch(fork()){
			case -1:
				sysfatal("fork: %r");
			case 0:
				close(p[1]);
				while(n = read(p[0], buf, sizeof(buf))){
					write(1, buf, n);
					write(fd, buf, n);
				}
				close(p[0]);
				exits(nil);
			}
			close(p[0]);
			dup(p[1], 1);
			close(p[1]);
		}
		else{
			dup(fd, 1);
			close(fd);
		}
		execl("/bin/games/opl3", "opl3", "-r", "1000", nil);
		sysfatal("execl: %r");
	}
	close(fd);
	close(p[1]);
	for(i = 1; i < nelem(reg); i++){
		s[5 * i] = reg[i];
		s[5 * i + 2] = val[i];
	}
	write(p[0], s, sizeof(s));
	s[0] = s[5] = 0xBD;
	incr = 60000 / bpm / 4;	/* milliseconds */
	s[7] = val[0] | seq[0];
	s[8] = incr;
	s[9] = incr >> 8;
	incr *= 1000000;	/* nanoseconds */
	write(p[0], s + 5, 5);
	t = nsec();
	for(oplon = 1; oplon; flushimage(display, 1)){
		t += incr;
		zzz = (t - nsec()) / 1000000;
		if(zzz > 1)
			sleep(zzz - 1);
		seqpos = (seqpos + 1) % seqlen;
		s[2] = s[7] ^ seq[seqpos];
		s[7] = val[0] | seq[seqpos];
		write(p[0], s, 10);
		draw(screen, rectaddpt(cell, seqpt), bg, nil, ZP);
		seqpt.x = seqpos ? seqpt.x + cell.max.x : seqr.min.x;
		draw(screen, rectaddpt(cell, seqpt), fg, nil, ZP);
	}
	close(p[0]);
	draw(screen, rectaddpt(cell, seqpt), bg, nil, ZP);
	seqpos = 0;
	seqpt.x = seqr.min.x;
	draw(screen, rectaddpt(cell, seqpt), fg, nil, ZP);
	flushimage(display, 1);
	exits(nil);
}
void
usage(void){
	sysfatal("usage: %s [-r file] [-d druminfo] [-p pattern]", argv0);
}
void
threadmain(int argc, char **argv){
	Point pt;
	Rune k;
	char lens[] = "Len: 16\0", bpms[] = "BPM: 80\0";
	int i, j;
	enum{RESIZE, MOUSE, KEYBOARD};
	Alt a[] = {
		[RESIZE]{nil, nil, CHANRCV},
		[MOUSE]{nil, nil, CHANRCV},
		[KEYBOARD]{nil, &k, CHANRCV},
		{nil, nil, CHANEND}
	};
	ARGBEGIN{
	case 'r':
		tee = 1;
		break;
	case 'd':
		if((i = open(EARGF(usage), OREAD)) < 0)
			sysfatal("open: %r");
		j = read(i, val, sizeof(val));
		close(i);
		if(j != sizeof(val))
			sysfatal("short read: wanted %d bytes, got %d: %r", sizeof(val), j);
		break;
	case 'p':
		if((i = open(EARGF(usage), OREAD)) < 0)
			sysfatal("open: %r");
		seqlen = read(i, seq, sizeof(seq));
		close(i);
		if(seqlen < 1)
			sysfatal("short read: %r");
		break;
	default:
		usage();
	}ARGEND
	if(initdraw(nil, nil, argv0) < 0)
		sysfatal("initdraw: %r");
	box = Rect(0, 0, Dx(screen->r) / 7, font->height);
	bg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DDarkgreen);
	fg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DPaleyellow);
	if(bg == nil || fg == nil)
		sysfatal("allocimage: %r");
	if((โŒ– = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	a[RESIZE].c = โŒ–->resizec;
	a[MOUSE].c = โŒ–->c;
	a[MOUSE].v = &โŒ–->Mouse;
	if((kbd = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");
	a[KEYBOARD].c = kbd->c;
	for(i = 0; i < 6; i++){
		m[i].lasthit = val[i + 10] >> 7;		/* tremolo */
		m[6 + i].lasthit = val[i + 10] >> 6 & 1;		/* vibrato */
		m[12 + i].lasthit = val[i + 10] >> 5 & 1;		/* sustaining */
		m[18 + i].lasthit = val[i + 10] & 0xF;		/* multiplier */
		m[24 + i].lasthit = val[i + 16] >> 4 & 0xF;	/* attack */
		m[30 + i].lasthit = val[i + 16] & 0xF;		/* decay */
		m[36 + i].lasthit = val[i + 22] >> 4 & 0xF;	/* sustain */
		m[42 + i].lasthit = val[i + 22] & 0xF;		/* release */
		m[48 + i].lasthit = val[i + 28];		/* wave */
		m[54 + i].lasthit = val[i + 34];		/* volume */
	}
	/* pitches */
	j = val[4] << 8 | val[1];
	for(i = nelem(freq) - 1; i && j != freq[i]; i--)
		;
	m[61].lasthit = i;
	j = val[5] << 8 | val[2];
	for(i = nelem(freq) - 1; i && j != freq[i]; i--)
		;
	m[62].lasthit = i;
	j = val[6] << 8 | val[3];
	for(i = nelem(freq) - 1; i && j != freq[i]; i--)
		;
	m[63].lasthit = i;
	m[66].lasthit = val[7] & 1;	/* synth type */
	m[67].lasthit = val[7] >> 1 & 7;	/* multiplier */
	m[68].lasthit = val[0] >> 7;	/* tremolo depth */
	m[69].lasthit = val[0] >> 6 & 1;	/* vibrato depth */
	sendul(โŒ–->resizec, 1);
	for(;;)
		switch(alt(a)){
		case MOUSE:
			if(!โŒ–->buttons)
				break;
			if(ptinrect(โŒ–->xy, seqr)){
				i = (โŒ–->xy.x - seqr.min.x) / cell.max.x;
				j = (โŒ–->xy.y - seqr.min.y) / font->height;
				if(i < 0 || i >= seqlen || j < 0 || j >= 5)
					break;
				do readmouse(โŒ–); while(โŒ–->buttons);
				if((โŒ–->xy.x - seqr.min.x) / cell.max.x == i
				&& (โŒ–->xy.y - seqr.min.y) / font->height == j)
				seq[i] ^= 1 << j;
				draw(screen, rectaddpt(cell, addpt(seqr.min, Pt(i * cell.max.x, j * font->height))),
					seq[i] >> j & 1 ? fg : bg, nil, ZP);
				flushimage(display, 1);
				break;
			}
			if(oplon){
				oplon = 0;
				break;
			}
			if(โŒ–->buttons == 4){
				j = menuhit(3, โŒ–, &b3, nil);
				if(j < 0)
					break;
				if(j == 2)
					threadexitsall(nil);
				*err = 0;
				enter(mb3[0], err, ERRMAX, โŒ–, kbd, nil);
				if((i = create(err, OWRITE, 0666)) < 0){
					*err = 0;
					errstr(err, ERRMAX);
					enter(err, nil, 0, โŒ–, kbd, nil);
					break;
				}
				if(j)
					write(i, val, sizeof(val));
				else
					write(i, seq, seqlen);
				close(i);
				break;
			}
			if(โŒ–->buttons != 1)
				break;
			i = (โŒ–->xy.x - screen->r.min.x) / box.max.x - 1;
			j = (โŒ–->xy.y - screen->r.min.y) / font->height - 1;
			if(i < 0 || j < 0 || i >= 6 || j >= nelem(m) / 6 || j == 10 && (i == 0 || i > 3))
				break;
			pt.x = screen->r.min.x + (i + 1) * box.max.x;
			pt.y = screen->r.min.y + (j + 1) * font->height;
			if(j == 11 && i == 4){
				strcpy(err, bpms + 5);
				enter("BPM", err, 4, โŒ–, kbd, nil);
				i = atoi(err);
				if(i < 1 || i > 999)
					break;
				bpm = i;
				strcpy(bpms + 5, err);
				draw(screen, rectaddpt(box, pt), bg, nil, ZP);
				string(screen, pt, fg, ZP, font, bpms);
				flushimage(display, 1);
				break;
			}
			else if(j == 11 && i == 5){
				strcpy(err, lens + 5);
				enter("Len", err, 4, โŒ–, kbd, nil);
				i = atoi(err);
				if(i < 1 || i >= sizeof(seq))
					break;
				seqlen = i;
				strcpy(lens + 5, err);
				sendul(โŒ–->resizec, 1);
				break;
			}
			if(menuhit(1, โŒ–, m + j * 6 + i, nil) < 0)
				break;
			draw(screen, rectaddpt(box, pt), bg, nil, ZP);
			string(screen, pt, fg, ZP, font, m[j * 6 + i].item[m[j * 6 + i].lasthit]);
			flushimage(display, 1);
			switch(j){
			case 0: case 1: case 2: case 3:	/* trem, vib, sus, mult */
				val[i + 10] = m[i].lasthit << 7
					| m[i + 6].lasthit << 6
					| m[i + 12].lasthit << 5
					| m[i + 18].lasthit;
				break;
			case 4: case 5:		/* attack, decay */
				val[i + 16] = m[i + 24].lasthit << 4 | m[i + 30].lasthit;
				break;
			case 6: case 7:		/* sustain, release */
				val[i + 22] = m[i + 36].lasthit << 4 | m[i + 42].lasthit;
				break;
			case 8:		/* wave */
				val[i + 28] = m[i + 48].lasthit;
				break;
			case 9:	/* volume */
				val[i + 34] = m[i + 54].lasthit;
				break;
			default:
				switch(j * 6 + i){
				case 61:	/* BD pitch */
					val[1] = freq[m[61].lasthit];
					val[4] = freq[m[61].lasthit] >> 8;
					break;
				case 62:	/* SD pitch */
					val[2] = freq[m[62].lasthit];
					val[5] = freq[m[62].lasthit] >> 8;
					break;
				case 63:	/* TT pitch */
					val[3] = freq[m[63].lasthit];
					val[6] = freq[m[63].lasthit] >> 8;
					break;
				case 66: case 67:	/* BD synth type & mult */
					val[7] = 0x30 | m[67].lasthit << 1 | m[66].lasthit;
					break;
				case 68: case 69:	/* tremolo & vibrato depth */
					val[0] = 0x20 | m[68].lasthit << 7 | m[69].lasthit << 6;
					break;
				}
				break;
			}
			break;
		case RESIZE:
			oplon = 0;
			if(getwindow(display, Refnone) < 0)
				sysfatal("getwindow: %r");
			draw(screen, screen->r, bg, nil, ZP);
			box = Rect(0, 0, Dx(screen->r) / 7, font->height);
			pt = Pt(screen->r.min.x + box.max.x, screen->r.min.y);
			string(screen, pt, fg, ZP, font, "KICK Mod");
			pt.x += box.max.x;
			string(screen, pt, fg, ZP, font, "KICK Car");
			pt.x += box.max.x;
			string(screen, pt, fg, ZP, font, "SNARE");
			pt.x += box.max.x;
			string(screen, pt, fg, ZP, font, "TOM TOM");
			pt.x += box.max.x;
			string(screen, pt, fg, ZP, font, "CYMBAL");
			pt.x += box.max.x;
			string(screen, pt, fg, ZP, font, "HI HAT");
			pt.x = screen->r.min.x;
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Tremolo:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Vibrato:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Sustaining:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Multiplier:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Attack:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Decay:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Sustain:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Release:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Wave:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Volume:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Pitch:");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "Misc:");
			seqr.min.y = pt.y += 2 * font->height;
			string(screen, pt, fg, ZP, font, "HI HAT");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "CYMBAL");
			pt.y += font->height;
			seqpt = string(screen, pt, fg, ZP, font, "TOM TOM ");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "SNARE");
			pt.y += font->height;
			string(screen, pt, fg, ZP, font, "KICK");			
			seqr.min.x = seqpt.x;
			seqr.max.y = pt.y + font->height + 1;
			cell = Rect(1, 1, (screen->r.max.x - seqr.min.x) / seqlen, font->height);
			seqr.max.x = seqr.min.x + seqlen * cell.max.x + 1;
			draw(screen, seqr, display->black, nil, ZP);
			for(pt.y = seqr.min.y, j = 0; j < 5; j++, pt.y += font->height)
				for(pt.x = seqr.min.x, i = 0; i < seqlen; i++, pt.x += cell.max.x)
					draw(screen, rectaddpt(cell, pt), seq[i] >> j & 1 ? fg : bg, nil, ZP);
			seqpt = Pt(seqr.min.x + seqpos * cell.max.x, seqr.max.y);
			draw(screen, rectaddpt(cell, seqpt), fg, nil, ZP);
			pt = Pt(screen->r.min.x, screen->r.min.y + font->height);
			for(i = 0; i < nelem(m) - 2; i++){
				pt.x += box.max.x;
				if(m[i].item)
					string(screen, pt, fg, ZP, font, m[i].item[m[i].lasthit]);
				if(i % 6 == 5){
					pt.x = screen->r.min.x;
					pt.y += font->height;
				}
			}
			pt.x += box.max.x;
			string(screen, pt, fg, ZP, font, bpms);
			pt.x += box.max.x;
			string(screen, pt, fg, ZP, font, lens);
			flushimage(display, 1);
			break;
		case KEYBOARD:
			if(k == Kdel)
				threadexitsall(nil);
			if(oplon)
				oplon = 0;
			else if(proccreate(oplproc, nil, mainstacksize) < 0){
				*err = 0;
				errstr(err, ERRMAX);
				enter(err, nil, 0, โŒ–, kbd, nil);
			}
			break;
		default:
			sysfatal("Floronius binet ac miles leg VII hic fuit\n"
			"neque mulieres scierunt nisi paucae et ses erunt");
		}
}