]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gb/apu.c
games/gb: limit audio range to prevent clipping
[plan9front.git] / sys / src / games / gb / apu.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "dat.h"
5 #include "fns.h"
6
7 Event evsamp;
8 extern Event evenv, evwave;
9 s16int sbuf[2*4000], *sbufp;
10 enum {
11         Freq = 44100,
12         SRATEDIV = FREQ / Freq
13 };
14 static int fd;
15
16 u16int envmod;
17 u8int sweepen, sweepcalc, sweepctr;
18 u16int sweepfreq;
19 typedef struct chan chan;
20 struct chan {
21         u8int n, ectr;
22         u16int len;
23         u8int *env, *freq;
24         u16int fctr, fthr;
25         u32int finc;
26         u8int vol;
27 };
28 u8int wpos;
29 u16int lfsr;
30 u8int apustatus;
31 ulong waveclock;
32 u8int wavebuf;
33
34 chan sndch[4] = {
35         {
36                 .n = 0,
37                 .env = reg + NR12,
38                 .freq = reg + NR14,
39         },
40         {
41                 .n = 1,
42                 .env = reg + NR22,
43                 .freq = reg + NR24,
44         },
45         {
46                 .n = 2,
47         },
48         {
49                 .n = 3,
50                 .env = reg + NR42,
51                 .freq = reg + NR44,
52         }
53 };
54
55 Var apuvars[] = {
56         VAR(apustatus), VAR(envmod), VAR(sweepen), VAR(sweepcalc),
57         VAR(sweepctr), VAR(sweepfreq), VAR(wpos), VAR(lfsr), VAR(waveclock), VAR(wavebuf),
58         VAR(sndch[0].ectr), VAR(sndch[0].len), VAR(sndch[0].fctr), VAR(sndch[0].fthr), VAR(sndch[0].finc), VAR(sndch[0].vol),
59         VAR(sndch[1].ectr), VAR(sndch[1].len), VAR(sndch[1].fctr), VAR(sndch[1].fthr), VAR(sndch[1].finc), VAR(sndch[1].vol),
60         VAR(sndch[2].ectr), VAR(sndch[2].len), VAR(sndch[2].fctr), VAR(sndch[2].fthr), VAR(sndch[2].finc), VAR(sndch[2].vol),
61         VAR(sndch[3].ectr), VAR(sndch[3].len), VAR(sndch[3].fctr), VAR(sndch[3].fthr), VAR(sndch[3].finc), VAR(sndch[3].vol),
62         {nil, 0, 0},
63 };
64
65 void
66 rate(int i, u16int v)
67 {
68         switch(i){
69         case 0: case 1:
70                 sndch[i].finc = 131072ULL * 65536 / (Freq * (2048 - (v & 0x7ff)));
71                 break;
72         case 2:
73                 sndch[2].finc = 4 * (2048 - (v & 0x7ff));
74                 break;
75         case 3:
76                 sndch[3].finc = 524288ULL * 65536 / Freq;
77                 if((v & 7) != 0)
78                         sndch[3].finc /= v & 7;
79                 else
80                         sndch[3].finc <<= 1;
81                 sndch[3].finc >>= (v >> 4 & 15) + 1;
82         }
83 }
84
85 void
86 env(chan *c)
87 {
88         if((envmod & 1) == 0 && c->len > 0 && (*c->freq & 1<<6) != 0)
89                 if(--c->len == 0){
90                         apustatus &= ~(1<<c->n);
91                         c->vol = 0;
92                         return;
93                 }
94         if((apustatus & 1<<c->n) == 0 || (envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0)
95                 return;
96         c->ectr = *c->env & 7;
97         if((*c->env & 1<<3) != 0){
98                 if(c->vol < 15)
99                         c->vol++;
100         }else
101                 if(c->vol > 0)
102                         c->vol--;
103 }
104
105 void
106 wavetick(void *)
107 {
108         addevent(&evwave, sndch[2].finc);
109         wpos = wpos + 1 & 31;
110         wavebuf = reg[WAVE + (wpos >> 1)];
111         waveclock = clock;
112 }
113
114 s8int
115 wavesamp(void)
116 {
117         u8int x;
118
119         if((apustatus & 1<<4) == 0)
120                 return 0;
121         x = wavebuf;
122         if((wpos & 1) == 0)
123                 x >>= 4;
124         else
125                 x &= 0xf;
126         if((reg[NR32] & 3<<5) == 0)
127                 x = 0;
128         else
129                 x = x >> (reg[NR32] >> 5 & 3) - 1;
130         return x;
131 }
132
133 s8int
134 lfsrsamp(void)
135 {
136         int v;
137         u16int l;
138
139         sndch[3].fctr = v = sndch[3].fctr + sndch[3].finc;
140         for(;;){
141                 l = lfsr;
142                 v -= 0x10000;
143                 if(v < 0)
144                         break;
145                 lfsr >>= 1;
146                 if(((l ^ lfsr) & 1) != 0)
147                         if((reg[NR43] & 1<<3) != 0)
148                                 lfsr |= 0x40;
149                         else
150                                 lfsr |= 0x4000;
151         }
152         if((l & 1) != 0)
153                 return 0;
154         else
155                 return sndch[3].vol;
156 }
157
158 void
159 sweep(int wb)
160 {
161         u16int fr;
162         int d;
163         u16int cnt;
164         
165         cnt = reg[NR10];
166         d = sweepfreq >> (cnt & 7);
167         if((cnt & 1<<3) != 0)
168                 d = -d;
169         fr = sweepfreq + d;
170         sweepcalc |= cnt;
171         if(fr > 2047){
172                 sndch[0].len = 0;
173                 sndch[0].vol = 0;
174                 apustatus &= ~1;
175                 sweepen = 0;
176         }else if(wb && (cnt & 7) != 0){
177                 sweepfreq = fr;
178                 reg[NR13] = fr;
179                 reg[NR14] = reg[NR14] & 0xf8 | fr >> 8;
180                 rate(0, fr);
181                 sweep(0);
182         }
183 }
184
185 void
186 sndstart(chan *c, u8int v)
187 {
188         u8int cnt;
189
190         c->vol = *c->env >> 4;
191         c->ectr = *c->env & 7;
192         if(c->len == 0)
193                 c->len = 64;
194         apustatus |= 1<<c->n;
195         if(c == sndch){
196                 cnt = reg[NR10];
197                 sweepen = (cnt & 0x07) != 0 || (cnt & 0x70) != 0;
198                 sweepctr = cnt >> 4 & 7;
199                 sweepctr += sweepctr - 1 & 8;
200                 sweepfreq = v << 8 & 0x700 | reg[NR13];
201                 sweepcalc = 0;
202                 if((cnt & 0x07) != 0)
203                         sweep(0);
204         }
205         if((*c->freq & 0x40) == 0 && (v & 0x40) != 0 && (envmod & 1) != 0 && --c->len == 0 || (*c->env & 0xf8) == 0){
206                 apustatus &= ~(1<<c->n);
207                 c->vol = 0;
208         }
209 }
210
211 void
212 envtick(void *)
213 {
214         addevent(&evenv, FREQ / 512);
215
216         env(&sndch[0]);
217         env(&sndch[1]);
218         if((envmod & 1) == 0 && sndch[2].len > 0 && (reg[NR34] & 0x40) != 0)
219                 if(--sndch[2].len == 0){
220                         apustatus &= ~4;
221                         delevent(&evwave);
222                 }
223         env(&sndch[3]);
224         if((envmod & 3) == 2 && sweepen && --sweepctr == 0){
225                 sweepctr = reg[NR10] >> 4 & 7;
226                 sweepctr += sweepctr - 1 & 8;
227                 if((reg[NR10] & 0x70) != 0)
228                         sweep(1);
229         }
230         envmod++;
231 }
232
233 void
234 sampletick(void *)
235 {
236         u8int cntl, cnth;
237         s16int ch[4];
238         s16int s[2];
239         int i;
240         
241         addevent(&evsamp, SRATEDIV);
242         
243         sndch[0].fctr += sndch[0].finc;
244         if(sndch[0].fctr >= sndch[0].fthr)
245                 ch[0] = sndch[0].vol;
246         else
247                 ch[0] = 0;
248         sndch[1].fctr += sndch[1].finc;
249         if(sndch[1].fctr >= sndch[1].fthr)
250                 ch[1] = sndch[1].vol;
251         else
252                 ch[1] = 0;
253         ch[2] = wavesamp();
254         ch[3] = lfsrsamp();
255         
256         cntl = reg[NR50];
257         cnth = reg[NR51];
258         s[0] = 0;
259         s[1] = 0;
260         for(i = 0; i < 4; i++){
261                 if(i == 2 ? ((reg[NR30] & 0x80) == 0) : ((*sndch[i].env & 0xf8) == 0))
262                         continue;
263                 ch[i] = ch[i] * 2 - 15;
264                 if((cnth & 1<<i) != 0)
265                         s[0] += ch[i];
266                 if((cnth & 1<<4<<i) != 0)
267                         s[1] += ch[i];
268         }
269         s[0] *= 1 + (cntl & 7);
270         s[1] *= 1 + (cntl >> 4 & 7);
271         
272         if(sbufp < sbuf + nelem(sbuf)){
273                 sbufp[0] = s[0] * 30;
274                 sbufp[1] = s[1] * 30;
275                 sbufp += 2;
276         }
277 }
278
279 void
280 sndwrite(u8int a, u8int v)
281 {
282         static u16int thr[4] = {0x2000, 0x4000, 0x8000, 0xC000};
283         static u8int clrreg[] = {
284                 0x80, 0x3f, 0x00, 0xff, 0xbf,
285                 0xff, 0x3f, 0x00, 0xff, 0xbf,
286                 0x7f, 0xff, 0x9f, 0xff, 0xbf,
287                 0xff, 0xff, 0x00, 0x00, 0xbf,
288                 0x00, 0x00
289         };
290         
291         if((reg[NR52] & 0x80) == 0 && a != NR52 && ((mode & CGB) != 0 || a != NR11 && a != NR21 && a != NR31 && a != NR41))
292                 return;
293         switch(a){
294         case NR10:
295                 if((sweepcalc & 0x08) != 0 && (reg[NR10] & ~v & 0x08) != 0){
296                         sndch[0].vol = 0;
297                         apustatus &= ~1;
298                         sweepcalc = 0;
299                 }
300                 break;
301         case NR11:
302                 sndch[0].fthr = thr[v >> 6 & 3];
303                 sndch[0].len = 64 - (v & 63);
304                 break;
305         case NR12:
306                 if((v & 0xf8) == 0){
307                         sndch[0].vol = 0;
308                         apustatus &= ~1;
309                 }
310                 break;
311         case NR13:
312                 rate(0, reg[NR14] << 8 & 0x700 | v);
313                 break;
314         case NR14:
315                 rate(0, v << 8 & 0x700 | reg[NR13]);
316                 if((v & 1<<7) != 0)
317                         sndstart(&sndch[0], v);
318                 break;
319         case NR21:
320                 sndch[1].fthr = thr[v >> 6 & 3];
321                 sndch[1].len = 64 - (v & 63);
322                 break;
323         case NR22:
324                 if((v & 0xf8) == 0){
325                         sndch[1].vol = 0;
326                         apustatus &= ~2;
327                 }
328                 break;
329         case NR23:
330                 rate(1, reg[NR24] << 8 & 0x700 | v);
331                 break;
332         case NR24:
333                 rate(1, v << 8 & 0x700 | reg[NR23]);
334                 if((v & 1<<7) != 0)
335                         sndstart(&sndch[1], v);
336                 break;
337         case NR30:
338                 if((v & 0x80) == 0){
339                         apustatus &= ~4;
340                         delevent(&evwave);
341                 }
342                 break;
343         case NR31:
344                 sndch[2].len = 256 - (v & 0xff);
345                 break;
346         case NR33:
347                 rate(2, reg[NR34] << 8 & 0x700 | v);
348                 break;
349         case NR34:
350                 rate(2, v << 8 & 0x700 | reg[NR33]);
351                 if((v & 0x80) != 0){
352                         if(sndch[2].len == 0)
353                                 sndch[2].len = 256;
354                         wpos = 0;
355                         if((reg[NR30] & 0x80) != 0){
356                                 apustatus |= 4;
357                                 delevent(&evwave);
358                                 addevent(&evwave, sndch[2].finc);
359                         }
360                 }
361                 break;
362         case NR41:
363                 sndch[3].len = 64 - (v & 63);
364                 break;
365         case NR42:
366                 if((v & 0xf8) == 0){
367                         sndch[3].vol = 0;
368                         apustatus &= ~8;
369                 }
370                 break;
371         case NR43:
372                 rate(3, v);
373                 break;
374         case NR44:
375                 if((v & 1<<7) != 0){
376                         if((reg[NR43] & 1<<3) != 0)
377                                 lfsr = 0x7f;
378                         else
379                                 lfsr = 0x7fff;
380                         sndstart(&sndch[3], v);
381                 }
382                 break;
383         case NR52:
384                 apustatus = v & 0xf0 | apustatus & 0x0f;
385                 if((v & 0x80) == 0){
386                         memcpy(reg + NR10, clrreg, NR52 - NR10);
387                         if((mode & CGB) != 0){
388                                 sndch[0].len = 0;
389                                 sndch[1].len = 0;
390                                 sndch[2].len = 0;
391                                 sndch[3].len = 0;
392                                 apustatus = 0;
393                                 delevent(&evwave);
394                         }
395                 }else if((reg[NR52] & 0x80) == 0){
396                         envmod = 0;
397                         delevent(&evenv);
398                         addevent(&evenv, FREQ / 512);
399                         sndch[0].fctr = 0;
400                         sndch[1].fctr = 0;
401                         sndch[3].fctr = 0;
402                 }
403         }
404         reg[a] = v;
405 }
406
407 u8int
408 waveread(u8int a)
409 {
410         if((apustatus & 4) != 0)
411                 if((mode & CGB) != 0 || clock - waveclock == 0)
412                         return wavebuf;
413                 else
414                         return 0xff;
415         return reg[WAVE + a];
416 }
417
418 void
419 wavewrite(u8int a, u8int v)
420 {
421         reg[WAVE + a] = v;
422 }
423
424 void
425 audioinit(void)
426 {
427         fd = open("/dev/audio", OWRITE);
428         if(fd < 0)
429                 sysfatal("open: %r");
430         sbufp = sbuf;
431         evsamp.f = sampletick;
432         addevent(&evsamp, SRATEDIV);
433 }
434
435 int
436 audioout(void)
437 {
438         int rc;
439         static int cl;
440
441         if(sbufp == nil)
442                 return -1;
443         if(sbufp == sbuf)
444                 return 0;
445         cl = clock;
446         rc = write(fd, sbuf, (sbufp - sbuf) * 2);
447         if(rc > 0)
448                 sbufp -= (rc+1)/2;
449         if(sbufp < sbuf)
450                 sbufp = sbuf;
451         return 0;
452 }