diff options
author | aiju <aiju@phicode.de> | 2015-04-12 14:55:25 +0200 |
---|---|---|
committer | aiju <aiju@phicode.de> | 2015-04-12 14:55:25 +0200 |
commit | e8221d07d8c9932393f76758363327c07bdbc2dd (patch) | |
tree | 74feae6127e9d611a33a166d0d759258a5177567 /sys/src/games | |
parent | d009b0013d90c1b3ecd0b3f35443f811ccee2791 (diff) |
games/gb: improve sound emulation by modelling analog behaviour
Diffstat (limited to 'sys/src/games')
-rw-r--r-- | sys/src/games/gb/apu.c | 310 | ||||
-rw-r--r-- | sys/src/games/gb/dat.h | 2 | ||||
-rw-r--r-- | sys/src/games/gb/ev.c | 12 | ||||
-rw-r--r-- | sys/src/games/gb/gb.c | 5 |
4 files changed, 185 insertions, 144 deletions
diff --git a/sys/src/games/gb/apu.c b/sys/src/games/gb/apu.c index 0501b0db6..b01c2f80d 100644 --- 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,29 +20,31 @@ u8int sweepen, sweepcalc, sweepctr; 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; u8int apustatus; ulong waveclock; u8int wavebuf; +double samp[2]; chan sndch[4] = { { .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,114 +53,171 @@ chan sndch[4] = { .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) 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; @@ -171,14 +232,15 @@ sweep(int wb) 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){ 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 @@ sndstart(chan *c, u8int v) { u8int cnt; + filter(0); c->vol = *c->env >> 4; c->ectr = *c->env & 7; if(c->len == 0) @@ -200,80 +263,46 @@ sndstart(chan *c, u8int v) 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 @@ sndwrite(u8int a, u8int v) } break; case NR11: - sndch[0].fthr = thr[v >> 6 & 3]; sndch[0].len = 64 - (v & 63); break; case NR12: @@ -309,15 +337,14 @@ sndwrite(u8int a, u8int v) } 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,35 +354,35 @@ sndwrite(u8int a, u8int v) } 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; case NR30: if((v & 0x80) == 0){ apustatus &= ~4; - delevent(&evwave); + delevent(&chev[2]); } break; case NR31: 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; 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 @@ sndwrite(u8int a, u8int v) } break; case NR43: - rate(3, v); + rate(&sndch[3], v); break; case NR44: if((v & 1<<7) != 0){ @@ -380,6 +407,9 @@ sndwrite(u8int a, u8int v) 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 @@ sndwrite(u8int a, u8int v) 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 @@ audioinit(void) sbufp = sbuf; evsamp.f = sampletick; addevent(&evsamp, SRATEDIV); + addevent(&chev[0], 8 * 2048); + addevent(&chev[1], 8 * 2048); + addevent(&chev[3], 8 * 2048); } int diff --git a/sys/src/games/gb/dat.h b/sys/src/games/gb/dat.h index 4bff393e5..428cfffb9 100644 --- a/sys/src/games/gb/dat.h +++ b/sys/src/games/gb/dat.h @@ -155,6 +155,6 @@ struct Var { }; #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]; diff --git a/sys/src/games/gb/ev.c b/sys/src/games/gb/ev.c index 20d84876e..e907143e0 100644 --- 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,11 +109,15 @@ eventinit(void) extern void hblanktick(void *); extern void envtick(void *); extern void wavetick(void *); + extern void chantick(void *); evhblank.f = hblanktick; addevent(&evhblank, 240*4); 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; } diff --git a/sys/src/games/gb/gb.c b/sys/src/games/gb/gb.c index 3268634e9..e8bcdb459 100644 --- a/sys/src/games/gb/gb.c +++ b/sys/src/games/gb/gb.c @@ -222,6 +222,7 @@ keyproc(void *) static char buf[256]; char *s; Rune r; + extern double TAU; fd = open("/dev/kbd", OREAD); if(fd < 0) @@ -240,6 +241,10 @@ keyproc(void *) } 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; |