shithub: riscv

Download patch

ref: e8221d07d8c9932393f76758363327c07bdbc2dd
parent: d009b0013d90c1b3ecd0b3f35443f811ccee2791
author: aiju <[email protected]>
date: Sun Apr 12 10:55:25 EDT 2015

games/gb: improve sound emulation by modelling analog behaviour

--- a/sys/src/games/gb/apu.c
+++ b/sys/src/games/gb/apu.c
@@ -4,8 +4,10 @@
 #include "dat.h"
 #include "fns.h"
 
+double TAU = 25000;
+
 Event evsamp;
-extern Event evenv, evwave;
+extern Event evenv;
 s16int sbuf[2*4000], *sbufp;
 enum {
 	Freq = 44100,
@@ -18,12 +20,11 @@
 u16int sweepfreq;
 typedef struct chan chan;
 struct chan {
-	u8int n, ectr;
-	u16int len;
 	u8int *env, *freq;
-	u16int fctr, fthr;
-	u32int finc;
-	u8int vol;
+	int per;
+	u16int len;
+	u8int n, ectr;
+	u8int vol, ctr, samp;
 };
 u8int wpos;
 u16int lfsr;
@@ -30,6 +31,7 @@
 u8int apustatus;
 ulong waveclock;
 u8int wavebuf;
+double samp[2];
 
 chan sndch[4] = {
 	{
@@ -36,11 +38,13 @@
 		.n = 0,
 		.env = reg + NR12,
 		.freq = reg + NR14,
+		.per = 8 * 2048,
 	},
 	{
 		.n = 1,
 		.env = reg + NR22,
 		.freq = reg + NR24,
+		.per = 8 * 2048,
 	},
 	{
 		.n = 2,
@@ -49,99 +53,132 @@
 		.n = 3,
 		.env = reg + NR42,
 		.freq = reg + NR44,
+		.per = 32
 	}
 };
+Event chev[4] = {
+	{.aux = &sndch[0]},
+	{.aux = &sndch[1]},
+	{.aux = &sndch[2]},
+	{.aux = &sndch[3]}
+};
 
 Var apuvars[] = {
 	VAR(apustatus), VAR(envmod), VAR(sweepen), VAR(sweepcalc),
 	VAR(sweepctr), VAR(sweepfreq), VAR(wpos), VAR(lfsr), VAR(waveclock), VAR(wavebuf),
-	VAR(sndch[0].ectr), VAR(sndch[0].len), VAR(sndch[0].fctr), VAR(sndch[0].fthr), VAR(sndch[0].finc), VAR(sndch[0].vol),
-	VAR(sndch[1].ectr), VAR(sndch[1].len), VAR(sndch[1].fctr), VAR(sndch[1].fthr), VAR(sndch[1].finc), VAR(sndch[1].vol),
-	VAR(sndch[2].ectr), VAR(sndch[2].len), VAR(sndch[2].fctr), VAR(sndch[2].fthr), VAR(sndch[2].finc), VAR(sndch[2].vol),
-	VAR(sndch[3].ectr), VAR(sndch[3].len), VAR(sndch[3].fctr), VAR(sndch[3].fthr), VAR(sndch[3].finc), VAR(sndch[3].vol),
+	VAR(sndch[0].ectr), VAR(sndch[0].len), VAR(sndch[0].per), VAR(sndch[0].ctr), VAR(sndch[0].vol), VAR(sndch[0].samp),
+	VAR(sndch[1].ectr), VAR(sndch[1].len), VAR(sndch[1].per), VAR(sndch[1].ctr), VAR(sndch[1].vol), VAR(sndch[1].samp),
+	VAR(sndch[2].ectr), VAR(sndch[2].len), VAR(sndch[2].per), VAR(sndch[2].vol), VAR(sndch[2].samp),
+	VAR(sndch[3].ectr), VAR(sndch[3].len), VAR(sndch[3].per), VAR(sndch[3].vol), VAR(sndch[3].samp),
 	{nil, 0, 0},
 };
 
-void
-rate(int i, u16int v)
+static void
+rate(chan *c, u16int v)
 {
-	switch(i){
+	switch(c->n){
 	case 0: case 1:
-		sndch[i].finc = 131072ULL * 65536 / (Freq * (2048 - (v & 0x7ff)));
+		c->per = 8 * (2048 - (v & 0x7ff));
 		break;
 	case 2:
-		sndch[2].finc = 4 * (2048 - (v & 0x7ff));
+		c->per = 4 * (2048 - (v & 0x7ff));
 		break;
 	case 3:
-		sndch[3].finc = 524288ULL * 65536 / Freq;
+		c->per = 32;
 		if((v & 7) != 0)
-			sndch[3].finc /= v & 7;
+			c->per *= v & 7;
 		else
-			sndch[3].finc <<= 1;
-		sndch[3].finc >>= (v >> 4 & 15) + 1;
+			c->per >>= 1;
+		c->per <<= (v >> 4 & 15);
 	}
 }
 
-void
-env(chan *c)
+static void
+filter(int t)
 {
-	if((envmod & 1) == 0 && c->len > 0 && (*c->freq & 1<<6) != 0)
-		if(--c->len == 0){
-			apustatus &= ~(1<<c->n);
-			c->vol = 0;
-			return;
-		}
-	if((apustatus & 1<<c->n) == 0 || (envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0)
-		return;
-	c->ectr = *c->env & 7;
-	if((*c->env & 1<<3) != 0){
-		if(c->vol < 15)
-			c->vol++;
-	}else
-		if(c->vol > 0)
-			c->vol--;
-}
+	static int ov0, ov1;
+	static u32int oclock;
+	double e;
+	u8int cntl, cnth;
+	int i, v;
 
-void
-wavetick(void *)
-{
-	addevent(&evwave, sndch[2].finc);
-	wpos = wpos + 1 & 31;
-	wavebuf = reg[WAVE + (wpos >> 1)];
-	waveclock = clock;
+	e = exp((clock + t - oclock) * -(TAU / FREQ));
+	samp[0] = e * samp[0] + (1 - e) * ov0;
+	samp[1] = e * samp[1] + (1 - e) * ov1;
+	oclock = clock + t;
+	cntl = reg[NR50];
+	cnth = reg[NR51];
+	ov0 = 0;
+	ov1 = 0;
+	for(i = 0; i < 4; i++){
+		if(i == 2 ? ((reg[NR30] & 0x80) == 0) : ((*sndch[i].env & 0xf8) == 0))
+			continue;
+		v = sndch[i].samp * 2 - 15;
+		if((cnth & 1<<i) != 0)
+			ov0 += v;
+		if((cnth & 1<<4<<i) != 0)
+			ov1 += v;
+	}
+	ov0 *= 1 + (cntl & 7);
+	ov1 *= 1 + (cntl >> 4 & 7);
 }
 
-s8int
-wavesamp(void)
+static void
+chansamp(chan *c, int t)
 {
-	u8int x;
-
-	if((apustatus & 1<<4) == 0)
-		return 0;
-	x = wavebuf;
-	if((wpos & 1) == 0)
-		x >>= 4;
-	else
-		x &= 0xf;
-	if((reg[NR32] & 3<<5) == 0)
-		x = 0;
-	else
-		x = x >> (reg[NR32] >> 5 & 3) - 1;
-	return x;
+	u8int ov;
+	
+	ov = c->samp;
+	switch(c->n){
+	case 0: case 1:
+		c->samp = c->vol;
+		switch(reg[NR21] >> 6){
+		case 0: if(c->ctr < 7) c->samp = 0; break;
+		case 1: if(c->ctr < 6) c->samp = 0; break;
+		case 2: if(c->ctr < 4) c->samp = 0; break;
+		case 3: if(c->ctr >= 6) c->samp = 0; break;
+		}
+		break;
+	case 2:
+		if((apustatus & 1<<4) == 0){
+			c->samp = 0;
+			break;
+		}
+		c->samp = wavebuf;
+		if((wpos & 1) == 0)
+			c->samp >>= 4;
+		else
+			c->samp &= 0xf;
+		if((reg[NR32] & 3<<5) == 0)
+			c->samp = 0;
+		else
+			c->samp = c->samp >> (reg[NR32] >> 5 & 3) - 1;
+		break;
+	case 3:
+		c->samp = (lfsr & 1) != 0 ? 0 : c->vol;
+	}
+	if(ov != c->samp)
+		filter(t);
 }
 
-s8int
-lfsrsamp(void)
+void
+chantick(void *vc)
 {
-	int v;
+	chan *c;
 	u16int l;
-
-	sndch[3].fctr = v = sndch[3].fctr + sndch[3].finc;
-	for(;;){
+	
+	c = vc;
+	switch(c->n){
+	case 0: case 1:
+		c->ctr = c->ctr - 1 & 7;
+		break;
+	case 2:
+		wpos = wpos + 1 & 31;
+		wavebuf = reg[WAVE + (wpos >> 1)];
+		waveclock = clock;
+		break;
+	case 3:
 		l = lfsr;
-		v -= 0x10000;
-		if(v < 0)
-			break;
 		lfsr >>= 1;
 		if(((l ^ lfsr) & 1) != 0)
 			if((reg[NR43] & 1<<3) != 0)
@@ -148,16 +185,40 @@
 				lfsr |= 0x40;
 			else
 				lfsr |= 0x4000;
+		break;
 	}
-	if((l & 1) != 0)
-		return 0;
-	else
-		return sndch[3].vol;
+	chansamp(c, chev[c->n].time);
+	addevent(&chev[c->n], c->per);
 }
 
-void
-sweep(int wb)
+static void
+env(chan *c, int t)
 {
+	if((envmod & 1) == 0 && c->len > 0 && (*c->freq & 1<<6) != 0)
+		if(--c->len == 0){
+			apustatus &= ~(1<<c->n);
+			c->vol = 0;
+			chansamp(c, t);
+			return;
+		}
+	if((apustatus & 1<<c->n) == 0 || (envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0)
+		return;
+	c->ectr = *c->env & 7;
+	if((*c->env & 1<<3) != 0){
+		if(c->vol < 15){
+			c->vol++;
+			chansamp(c, t);
+		}
+	}else
+		if(c->vol > 0){
+			c->vol--;
+			chansamp(c, t);
+		}
+}
+
+static void
+sweep(int wb, int t)
+{
 	u16int fr;
 	int d;
 	u16int cnt;
@@ -171,6 +232,7 @@
 	if(fr > 2047){
 		sndch[0].len = 0;
 		sndch[0].vol = 0;
+		chansamp(&sndch[0], t);
 		apustatus &= ~1;
 		sweepen = 0;
 	}else if(wb && (cnt & 7) != 0){
@@ -177,8 +239,8 @@
 		sweepfreq = fr;
 		reg[NR13] = fr;
 		reg[NR14] = reg[NR14] & 0xf8 | fr >> 8;
-		rate(0, fr);
-		sweep(0);
+		rate(&sndch[0], fr);
+		sweep(0, t);
 	}
 }
 
@@ -187,6 +249,7 @@
 {
 	u8int cnt;
 
+	filter(0);
 	c->vol = *c->env >> 4;
 	c->ectr = *c->env & 7;
 	if(c->len == 0)
@@ -200,80 +263,46 @@
 		sweepfreq = v << 8 & 0x700 | reg[NR13];
 		sweepcalc = 0;
 		if((cnt & 0x07) != 0)
-			sweep(0);
+			sweep(0, 0);
 	}
 	if((*c->freq & 0x40) == 0 && (v & 0x40) != 0 && (envmod & 1) != 0 && --c->len == 0 || (*c->env & 0xf8) == 0){
 		apustatus &= ~(1<<c->n);
 		c->vol = 0;
 	}
+	chansamp(c, 0);
 }
 
 void
 envtick(void *)
 {
-	addevent(&evenv, FREQ / 512);
-
-	env(&sndch[0]);
-	env(&sndch[1]);
+	env(&sndch[0], evenv.time);
+	env(&sndch[1], evenv.time);
 	if((envmod & 1) == 0 && sndch[2].len > 0 && (reg[NR34] & 0x40) != 0)
 		if(--sndch[2].len == 0){
 			apustatus &= ~4;
-			delevent(&evwave);
+			delevent(&chev[2]);
 		}
-	env(&sndch[3]);
+	env(&sndch[3], evenv.time);
 	if((envmod & 3) == 2 && sweepen && --sweepctr == 0){
 		sweepctr = reg[NR10] >> 4 & 7;
 		sweepctr += sweepctr - 1 & 8;
 		if((reg[NR10] & 0x70) != 0)
-			sweep(1);
+			sweep(1, evenv.time);
 	}
 	envmod++;
+	addevent(&evenv, FREQ / 512);
 }
 
 void
 sampletick(void *)
 {
-	u8int cntl, cnth;
-	s16int ch[4];
-	s16int s[2];
-	int i;
-	
-	addevent(&evsamp, SRATEDIV);
-	
-	sndch[0].fctr += sndch[0].finc;
-	if(sndch[0].fctr >= sndch[0].fthr)
-		ch[0] = sndch[0].vol;
-	else
-		ch[0] = 0;
-	sndch[1].fctr += sndch[1].finc;
-	if(sndch[1].fctr >= sndch[1].fthr)
-		ch[1] = sndch[1].vol;
-	else
-		ch[1] = 0;
-	ch[2] = wavesamp();
-	ch[3] = lfsrsamp();
-	
-	cntl = reg[NR50];
-	cnth = reg[NR51];
-	s[0] = 0;
-	s[1] = 0;
-	for(i = 0; i < 4; i++){
-		if(i == 2 ? ((reg[NR30] & 0x80) == 0) : ((*sndch[i].env & 0xf8) == 0))
-			continue;
-		ch[i] = ch[i] * 2 - 15;
-		if((cnth & 1<<i) != 0)
-			s[0] += ch[i];
-		if((cnth & 1<<4<<i) != 0)
-			s[1] += ch[i];
-	}
-	s[0] *= 1 + (cntl & 7);
-	s[1] *= 1 + (cntl >> 4 & 7);
-	
+	filter(evsamp.time);
 	if(sbufp < sbuf + nelem(sbuf)){
-		sbufp[0] = s[0] * 30;
-		sbufp[1] = s[1] * 30;
+		sbufp[0] = samp[0] * 30;
+		sbufp[1] = samp[1] * 30;
 		sbufp += 2;
 	}
+	addevent(&evsamp, SRATEDIV);
 }
 
 void
@@ -299,7 +328,6 @@
 		}
 		break;
 	case NR11:
-		sndch[0].fthr = thr[v >> 6 & 3];
 		sndch[0].len = 64 - (v & 63);
 		break;
 	case NR12:
@@ -309,15 +337,14 @@
 		}
 		break;
 	case NR13:
-		rate(0, reg[NR14] << 8 & 0x700 | v);
+		rate(&sndch[0], reg[NR14] << 8 & 0x700 | v);
 		break;
 	case NR14:
-		rate(0, v << 8 & 0x700 | reg[NR13]);
+		rate(&sndch[0], v << 8 & 0x700 | reg[NR13]);
 		if((v & 1<<7) != 0)
 			sndstart(&sndch[0], v);
 		break;
 	case NR21:
-		sndch[1].fthr = thr[v >> 6 & 3];
 		sndch[1].len = 64 - (v & 63);
 		break;
 	case NR22:
@@ -327,10 +354,10 @@
 		}
 		break;
 	case NR23:
-		rate(1, reg[NR24] << 8 & 0x700 | v);
+		rate(&sndch[1], reg[NR24] << 8 & 0x700 | v);
 		break;
 	case NR24:
-		rate(1, v << 8 & 0x700 | reg[NR23]);
+		rate(&sndch[1], v << 8 & 0x700 | reg[NR23]);
 		if((v & 1<<7) != 0)
 			sndstart(&sndch[1], v);
 		break;
@@ -337,7 +364,7 @@
 	case NR30:
 		if((v & 0x80) == 0){
 			apustatus &= ~4;
-			delevent(&evwave);
+			delevent(&chev[2]);
 		}
 		break;
 	case NR31:
@@ -344,10 +371,10 @@
 		sndch[2].len = 256 - (v & 0xff);
 		break;
 	case NR33:
-		rate(2, reg[NR34] << 8 & 0x700 | v);
+		rate(&sndch[2], reg[NR34] << 8 & 0x700 | v);
 		break;
 	case NR34:
-		rate(2, v << 8 & 0x700 | reg[NR33]);
+		rate(&sndch[2], v << 8 & 0x700 | reg[NR33]);
 		if((v & 0x80) != 0){
 			if(sndch[2].len == 0)
 				sndch[2].len = 256;
@@ -354,8 +381,8 @@
 			wpos = 0;
 			if((reg[NR30] & 0x80) != 0){
 				apustatus |= 4;
-				delevent(&evwave);
-				addevent(&evwave, sndch[2].finc);
+				delevent(&chev[2]);
+				addevent(&chev[2], sndch[2].per);
 			}
 		}
 		break;
@@ -369,7 +396,7 @@
 		}
 		break;
 	case NR43:
-		rate(3, v);
+		rate(&sndch[3], v);
 		break;
 	case NR44:
 		if((v & 1<<7) != 0){
@@ -380,6 +407,9 @@
 			sndstart(&sndch[3], v);
 		}
 		break;
+	case NR50: case NR51:
+		filter(0);
+		break;
 	case NR52:
 		apustatus = v & 0xf0 | apustatus & 0x0f;
 		if((v & 0x80) == 0){
@@ -390,15 +420,14 @@
 				sndch[2].len = 0;
 				sndch[3].len = 0;
 				apustatus = 0;
-				delevent(&evwave);
+				delevent(&chev[2]);
 			}
 		}else if((reg[NR52] & 0x80) == 0){
 			envmod = 0;
 			delevent(&evenv);
 			addevent(&evenv, FREQ / 512);
-			sndch[0].fctr = 0;
-			sndch[1].fctr = 0;
-			sndch[3].fctr = 0;
+			sndch[0].ctr = 0;
+			sndch[1].ctr = 0;
 		}
 	}
 	reg[a] = v;
@@ -430,6 +459,9 @@
 	sbufp = sbuf;
 	evsamp.f = sampletick;
 	addevent(&evsamp, SRATEDIV);
+	addevent(&chev[0], 8 * 2048);
+	addevent(&chev[1], 8 * 2048);
+	addevent(&chev[3], 8 * 2048);
 }
 
 int
--- a/sys/src/games/gb/dat.h
+++ b/sys/src/games/gb/dat.h
@@ -155,6 +155,6 @@
 };
 #define VAR(a) {&a, sizeof(a), 1}
 #define ARR(a) {a, sizeof(*a), nelem(a)}
-enum { NEVENT = 5 };
+enum { NEVENT = 8 };
 extern int (*mapper)(int, int);
 extern u32int moncols[4];
--- a/sys/src/games/gb/ev.c
+++ b/sys/src/games/gb/ev.c
@@ -4,9 +4,9 @@
 #include "dat.h"
 #include "fns.h"
 
-Event evhblank, evtimer, evenv, evwave;
-extern Event evsamp;
-Event *events[NEVENT] = {&evhblank, &evtimer, &evenv, &evsamp, &evwave};
+Event evhblank, evtimer, evenv;
+extern Event evsamp, chev[4];
+Event *events[NEVENT] = {&evhblank, &evtimer, &evenv, &evsamp, &chev[0], &chev[1], &chev[2], &chev[3]};
 Event *elist;
 static int timshtab[4] = {12, 4, 6, 8}, timsh;
 ulong timclock;
@@ -109,6 +109,7 @@
 	extern void hblanktick(void *);
 	extern void envtick(void *);
 	extern void wavetick(void *);
+	extern void chantick(void *);
 	
 	evhblank.f = hblanktick;
 	addevent(&evhblank, 240*4);
@@ -115,5 +116,8 @@
 	evtimer.f = timertick;
 	evenv.f = envtick;
 	addevent(&evenv, FREQ / 512);
-	evwave.f = wavetick;
+	chev[0].f = chantick;
+	chev[1].f = chantick;
+	chev[2].f = chantick;
+	chev[3].f = chantick;
 }
--- a/sys/src/games/gb/gb.c
+++ b/sys/src/games/gb/gb.c
@@ -222,6 +222,7 @@
 	static char buf[256];
 	char *s;
 	Rune r;
+	extern double TAU;
 
 	fd = open("/dev/kbd", OREAD);
 	if(fd < 0)
@@ -240,6 +241,10 @@
 			}
 			if(utfrune(buf, 't'))
 				trace = !trace;
+			if(utfrune(buf, KF|9))
+				TAU += 5000;
+			if(utfrune(buf, KF|10))
+				TAU -= 5000;
 		}
 		if(buf[0] != 'k' && buf[0] != 'K')
 			continue;