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

View Raw

More Information

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

#include <u.h>
#include <libc.h>
#include <thread.h>
/*
 * something to sit between orca(1) and opl3(1) for FM mayhem
 * http://www.fit.vutbr.cz/~arnost/opl/opl3.html
 *
 * There are 18 channels available on the opl3.  Percussion mode is not supported.
 * The first six channels may be in either 2-Op or 4-Op mode.
 * Enabling 4-Op mode disables one of the last six channels,
 * eg. If Channel 0 is in 4-Op mode, Channel c is disabled.
 *
 * Initial channel settings are loaded from the first 18 instruments
 * in the OP2-format instrument bank specified on the command line.
 * Double-voiced instruments are not supported, but 4-Op are: see op2ed.c
 *
 * Run something like "orca -u /fd/1 | orcopl bank" and send commands with the ; operator.

☞ There is one Global Command:

[tv][01]
	Deep Tremolo/Vibrato off/on

☞ Channel Commands are a channel identifier [0-h], followed by:

[CcDdEFfGgAaBx][0-8]
	Note Trigger: note (x = off), octave

b[ud][0-z]
	Bend Note Frequency: up/down, amount (in internal freq increments)

p[0123lrbs]
	Panning: 0 = Silent, 1 = l = Left, 2 = r = Right, 3 = b = s = Stereo

fb[0-7]
	Feedback: 0, ¹⁄₁₆, ⅛, ¼, ½, 1x, 2x, 4x

☞ Operator Commands are a ch. ident. [0-h], followed by op. ident. [0-3], followed by:

[tvhe][01]
	Operator Toggles: Tremolo, Vibrato, Hold sustain while key on, Envelope scaling

[adsr][0-f]
	Envelope Settings: Attack, Decay, Sustain, Release

m[0-f]
	Multiplier: 0 = ½, b = a, d = c, e = f

w[0-7]
	Wave Form: Sine, ½Sine, Abs. Sine, Pulse Sine, Even Sine, Even Abs. Sine, Sq., Der. Sq.

k[0-3]
	Key Scaling: 0 dB/oct, 1½ dB/oct, 3 dB/oct, 6 dB/oct
	
l[0-v]
	Level (in 2x internal increments)

☞ Multiple commands may be strung together after the same ; operator.
 */
int tee, note[18], 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
};
int ch[18] = {
	0x00000, 0x00100, 0x00200, 0x10000, 0x10100, 0x10200,
	0x00600, 0x00700, 0x00800, 0x10600, 0x10700, 0x10800,
	0x00300, 0x00400, 0x00500, 0x10300, 0x10400, 0x10500
};
int op[36] = {
	0x00000, 0x00100, 0x00200, 0x10000, 0x10100, 0x10200,
	0x01000, 0x01100, 0x01200, 0x11000, 0x11100, 0x11200,
	0x00800, 0x00900, 0x00A00, 0x10800, 0x10900, 0x10A00,
	0x00300, 0x00400, 0x00500, 0x10300, 0x10400, 0x10500,
	0x01300, 0x01400, 0x01500, 0x11300, 0x11400, 0x11500,
	0x00B00, 0x00C00, 0x00D00, 0x10B00, 0x10C00, 0x10D00
};
uchar reg104, regBD, regC0[18], reg20[36], reg40[36], reg60[36], reg80[36], regE0[36];
void
opl(void *arg){
	Channel *c;
	vlong t, zzz;
	int fd, p[2], i;
	uchar *buf, *e, s[36 * 5] = {1, 0, 0x20, 0, 0, 5, 1, 1, 0, 0, 0x04, 1, 0, 0, 0, 0xBD};
	ulong u;
	long n;
	c = arg;
	if((fd = open("/dev/audio", OWRITE)) < 0)
		sysfatal("open: %r");
	if(pipe(p) < 0)
		sysfatal("pipe: %r");
	switch(fork()){
	case -1:
		sysfatal("fork: %r");
	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", "100", nil);
		sysfatal("execl: %r");
	}
	close(fd);
	close(p[1]);
	s[12] = reg104;
	s[17] = regBD;
	for(i = 0; i < 18; i++){
		s[5 * i + 20] = ch[i] >> 8 | 0xC0;
		s[5 * i + 21] = ch[i] >> 16;
		s[5 * i + 22] = regC0[i];
	}
	write(p[0], s, 20 + 18 * 5);
	for(i = 0; i < 36; i++){
		s[5 * i] = op[i] >> 8 | 0x20;
		s[5 * i + 1] = op[i] >> 16;
		s[5 * i + 2] = reg20[i];
	}
	write(p[0], s, 36 * 5);
	for(i = 0; i < 36; i++){
		s[5 * i] ^= 0x60;
		s[5 * i + 2] = reg40[i];
	}
	write(p[0], s, 36 * 5);
	for(i = 0; i < 36; i++){
		s[5 * i] |= 0x20;
		s[5 * i + 2] = reg60[i];
	}
	write(p[0], s, 36 * 5);
	for(i = 0; i < 36; i++){
		s[5 * i] ^= 0xE0;
		s[5 * i + 2] = reg80[i];
	}
	write(p[0], s, 36 * 5);
	for(i = 0; i < 36; i++){
		s[5 * i] |= 0x60;
		s[5 * i + 2] = regE0[i];
	}
	write(p[0], s, 36 * 5);
	t = nsec();
	for(;;){
		for(e = s; e + 5 < s + sizeof(s) && (u = nbrecvul(c)); e += 5){
			if(u == 1)
				exits(nil);
			e[0] = u >> 8;
			e[1] = u >> 16;
			e[2] = u;
		}
		e[0] = e[1] = e[2] = 0;
		e[3] = 1;
		write(p[0], s, e - s + 5);
		e[3] = 0;
		t += 10000000;	/* hundredth of a second */
		zzz = (t - nsec()) / 1000000;
		if(zzz > 1)
			sleep(zzz - 1);
	}
}
void
threadmain(int argc, char *argv[]){
	char buf[256], *b;
	Channel *c;
	int i, n;
	ARGBEGIN{
	case 'r':
		tee = 1;
		break;
	default:
		sysfatal("usage: %s [-r] [bank]", argv0);
	}ARGEND
	if(argc != 1)
		sysfatal("usage: %s [-r] [bank]", argv0);
	if((b = malloc(8 + 36 * 18)) == nil)
		sysfatal("malloc: %r");
	if((i = open(*argv, OREAD)) < 0)
		sysfatal("open: %r");
	if(read(i, b, 8 + 36 * 18) != 8 + 36 * 18 || memcmp(b, "#OPL_II#", 8))
		sysfatal("not an OP2 bank!");
	close(i);
	regBD = b[9] & 0xC0;	/* first instr sets global wiggle depths */
	for(i = 0; i < 18; i++){
		regC0[i] = ~b[36 * i + 9] << 4 & 0x30 | b[36 * i + 18];
		reg20[i] = b[36 * i + 12];
		reg40[i] = b[36 * i + 16] | b[36 * i + 17];
		reg60[i] = b[36 * i + 13];
		reg80[i] = b[36 * i + 14];
		regE0[i] = b[36 * i + 15];
		reg20[i + 18] = b[36 * i + 19];
		reg40[i + 18] = b[36 * i + 23] | b[36 * i + 24];
		reg60[i + 18] = b[36 * i + 20];
		reg80[i + 18] = b[36 * i + 21];
		regE0[i + 18] = b[36 * i + 22];
	}
	for(i = 0; i < 6; i++)
		if(b[36 * i + 9] & 0x10){
			reg104 |= 1 << i;
			regC0[i + 12] = ~b[36 * i + 9] << 2 & 0x30 | b[36 * i + 34];
			reg20[i + 12] = b[36 * i + 28];
			reg40[i + 12] = b[36 * i + 32] | b[36 * i + 33];
			reg60[i + 12] = b[36 * i + 29];
			reg80[i + 12] = b[36 * i + 30];
			regE0[i + 12] = b[36 * i + 31];
			reg20[i + 30] = b[36 * i + 35];
			reg40[i + 30] = b[36 * i + 39] | b[36 * i + 40];
			reg60[i + 30] = b[36 * i + 36];
			reg80[i + 30] = b[36 * i + 37];
			regE0[i + 30] = b[36 * i + 38];
		}
	free(b);
	if((c = chancreate(sizeof(ulong), 8)) == nil)
		sysfatal("chancreate: %r");
	if(proccreate(opl, c, mainstacksize) < 0)
		sysfatal("proccreate: %r");
	while((n = read(0, buf, sizeof(buf))) > 0)
		for(buf[n] = 0, b = buf; *b; b += 4){
			i = -1;
			switch(b[0]){
			case 'v':
				i = -2;
			case 't':
				if(b[1] != '0' && b[1] != '1'){
					fprint(2, "unrecognised wiggle arg %c: %s\n", b[1], b);
					break;
				}
				regBD = regBD & 0x80 | (b[1] == '1') << 8 + i;
				sendul(c, 0xBD00 | regBD);
				b -= 2;
				continue;
			case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
				i = b[0] - '0';
				break;
			case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
				i = b[0] - 'a' + 10;
				break;
			default:
				fprint(2, "unrecognised cmd: %s\n", b);
				break;
			}
			if(i < 0)
				break;
			switch(b[1]){
			case 'p':
				switch(b[2]){
				case '0': n = 0x00; break;
				case '1': case 'l': n = 0x10; break;
				case '2': case 'r': n = 0x20; break;
				case '3': case 'b': case 's': n = 0x30; break;
				default: n = -1; break;
				}
				if(n == -1)
					break;
				regC0[i] = regC0[i] & 0x0F | n;
				sendul(c, 0xC000 | ch[i] | regC0[i]);
				b--;
				continue;
			case 'C': n = 0; goto note;
			case 'c': n = 1; goto note;
			case 'D': n = 2; goto note;
			case 'd': n = 3; goto note;
			case 'E': n = 4; goto note;
			case 'F': n = 5; goto note;
			case 'f':
				if(b[2] == 'b'){
					if(b[3] < '0' || b[3] > '7')
						break;
					regC0[i] = regC0[i] & 0x31 | b[3] - '0' << 1;
					sendul(c, 0xC000 | ch[i] | regC0[i]);
					continue;
				}
				n = 6;
				goto note;
			case 'G': n = 7; goto note;
			case 'g': n = 8; goto note;
			case 'A': n = 9; goto note;
			case 'a': n = 10; goto note;
			case 'B': n = 11; note:
				if(b[2] < '0' || b[2] > '8')
					break;
				n += 12 * (b[2] - '0');
				if(n >= nelem(freq))
					n = nelem(freq) - 1;
				sendul(c, 0xB000 | ch[i] | note[i] >> 8 & 0x1F);
				note[i] = freq[n] | 0x2000;
				sendul(c, 0xA000 | ch[i] | note[i] & 0xFF);
				sendul(c, 0xB000 | ch[i] | note[i] >> 8);
				b--;
				continue;
			case 'x':
				note[i] &= 0x1FFF;
				sendul(c, 0xB000 | ch[i] | note[i] >> 8);
				b--;
				continue;
			case 'b':
				if(b[3] >= '0' && b[3] <= '9')
					n = b[3] - '0';
				else if(b[3] >= 'a' && b[3] <= 'z')
					n = b[3] - 'a' + 10;
				else
					break;
				if(b[2] == 'd'){
					n = (note[i] & 0x1FFF) - n;
					if(n < 0)
						n = 0;
				}
				else if(b[2] == 'u'){
					n += note[i] & 0x1FFF;
					if(n > 0x1FFF)
						n = 0x1FFF;
				}
				else
					break;
				note[i] = note[i] & 0x2000 | n;
				sendul(c, 0xA000 | ch[i] | note[i] & 0xFF);
				sendul(c, 0xB000 | ch[i] | note[i] >> 8);
				continue;
			case '2': case '3':
				if(!(reg104 >> i & 1))
					break;
				i += 12;
				if(b[1] == '3')
			case '1':
				i += 18;
			case '0':
				switch(b[2]){
				case 't':
					if(b[3] != '0' && b[3] != '1')
						break;
					reg20[i] = reg20[i] & 0x7F | (b[3] == '1') << 7;
					sendul(c, 0x2000 | op[i] | reg20[i]);
					continue;
				case 'v':
					if(b[3] != '0' && b[3] != '1')
						break;
					reg20[i] = reg20[i] & 0xBF | (b[3] == '1') << 6;
					sendul(c, 0x2000 | op[i] | reg20[i]);
					continue;
				case 'h':
					if(b[3] != '0' && b[3] != '1')
						break;
					reg20[i] = reg20[i] & 0xDF | (b[3] == '1') << 5;
					sendul(c, 0x2000 | op[i] | reg20[i]);
					continue;
				case 'e':
					if(b[3] != '0' && b[3] != '1')
						break;
					reg20[i] = reg20[i] & 0xEF | (b[3] == '1') << 4;
					sendul(c, 0x2000 | op[i] | reg20[i]);
					continue;
				case 'm':
					if(b[3] >= '0' && b[3] <= '9')
						n = b[3] - '0';
					else if(b[3] >= 'a' && b[3] <= 'f')
						n = b[3] - 'a' + 10;
					else
						break;
					reg20[i] = reg20[i] & 0xF0 | n;
					sendul(c, 0x2000 | op[i] | reg20[i]);
					continue;
				case 'a':
					if(b[3] >= '0' && b[3] <= '9')
						n = b[3] - '0';
					else if(b[3] >= 'a' && b[3] <= 'f')
						n = b[3] - 'a' + 10;
					else
						break;
					reg60[i] = reg60[i] & 0x0F | n << 4;
					sendul(c, 0x6000 | op[i] | reg60[i]);
					continue;
				case 'd':
					if(b[3] >= '0' && b[3] <= '9')
						n = b[3] - '0';
					else if(b[3] >= 'a' && b[3] <= 'f')
						n = b[3] - 'a' + 10;
					else
						break;
					reg60[i] = reg60[i] & 0xF0 | n;
					sendul(c, 0x6000 | op[i] | reg60[i]);
					continue;
				case 's':
					if(b[3] >= '0' && b[3] <= '9')
						n = b[3] - '0';
					else if(b[3] >= 'a' && b[3] <= 'f')
						n = b[3] - 'a' + 10;
					else
						break;
					reg80[i] = reg80[i] & 0x0F | n << 4;
					sendul(c, 0x8000 | op[i] | reg80[i]);
					continue;
				case 'r':
					if(b[3] >= '0' && b[3] <= '9')
						n = b[3] - '0';
					else if(b[3] >= 'a' && b[3] <= 'f')
						n = b[3] - 'a' + 10;
					else
						break;
					reg80[i] = reg80[i] & 0xF0 | n;
					sendul(c, 0x8000 | op[i] | reg80[i]);
					continue;
				case 'w':
					if(b[3] < '0' && b[3] > '7')
						break;
					regE0[i] = b[3] - '0';
					sendul(c, 0xE000 | op[i] | regE0[i]);
					continue;
				case 'k':
					if(b[3] < '0' && b[3] > '3')
						break;
					reg40[i] = reg40[i] & 0x3F | b[3] - '0' << 6;
					sendul(c, 0x4000 | op[i] | reg40[i]);
					continue;
				case 'l':
					if(b[3] >= '0' && b[3] <= '9')
						n = b[3] - '0' << 1;
					else if(b[3] >= 'a' && b[3] <= 'v')
						n = b[3] - 'a' + 10 << 1;
					else
						break;
					reg40[i] = reg40[i] & 0xC0 | 0x3F - n;
					sendul(c, 0x4000 | op[i] | reg40[i]);
					continue;
				}
				break;
			}
			fprint(2, "bad cmd: %s\n", b);
			break;
		}
	sendul(c, 1);
	exits(nil);
}