ref: 17a9a0291b51a6f1bf84e8ee5fdb2f46db8519bb
dir: /sys/src/games/gb/audio.c/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include "dat.h"
#include "fns.h"
static int fd;
static int sc, ch1c, ch2c, ch3c, ch4c, ch4sr = 1, ch1vec, ch2vec, ch4vec, ch1v, ch2v, ch4v;
extern int paused;
enum { SAMPLE = 44100 };
static int
thresh(int f, int b)
{
switch(b){
case 0: return f/8;
case 1: return f/4;
case 2: return f/2;
default: return 3*f/4;
}
}
static int
freq(int lower)
{
int f;
f = mem[lower+1] & 7;
f = (f << 8) | mem[lower];
f = muldiv(2048 - f, SAMPLE, 131072);
return f;
}
static void
soundlen(int len, int ctrl, int n)
{
if(mem[ctrl] & 128){
mem[0xFF26] |= (1<<n);
mem[ctrl] &= ~128;
switch(n){
case 0:
ch1v = mem[0xFF12];
break;
case 1:
ch2v = mem[0xFF17];
break;
case 3:
ch4v = mem[0xFF21];
break;
}
}
if((mem[ctrl] & 64) == 0){
mem[0xFF26] |= (1<<n);
return;
}
if((mem[0xFF26] & (1<<n)) == 0)
return;
if(mem[len] == ((n == 2) ? 255 : 63)){
mem[0xFF26] &= ~(1<<n);
return;
}
mem[len]++;
}
static void
envelope(int *v, int *c)
{
int f;
f = (*v & 7) * SAMPLE / 64;
if(f == 0)
return;
if(*c >= f){
if(*v & 8){
if((*v >> 4) < 0xF)
*v += 0x10;
}else
if((*v >> 4) > 0)
*v -= 0x10;
*c = 0;
}
(*c)++;
}
static void
dosample(short *smp)
{
int ch1s, ch2s, ch3s, ch4s, ch1f, ch2f, ch3f, ch4f, k, r, s;
u8int f;
if(sc >= SAMPLE/256){
soundlen(0xFF11, 0xFF14, 0);
soundlen(0xFF16, 0xFF19, 1);
soundlen(0xFF1B, 0xFF1E, 2);
soundlen(0xFF20, 0xFF23, 3);
sc = 0;
}
sc++;
envelope(&ch1v, &ch1vec);
envelope(&ch2v, &ch2vec);
envelope(&ch4v, &ch4vec);
ch1f = freq(0xFF13);
if(ch1c >= ch1f)
ch1c = 0;
if(ch1c >= thresh(ch1f, mem[0xFF11] >> 6))
ch1s = 1;
else
ch1s = -1;
ch1s *= ch1v >> 4;
ch1s *= 8000 / 0xF;
ch1c++;
ch2f = freq(0xFF18);
if(ch2c >= ch2f)
ch2c = 0;
if(ch2c >= thresh(ch1f, mem[0xFF16] >> 6))
ch2s = 1;
else
ch2s = -1;
ch2s *= ch2v >> 4;
ch2s *= 8000 / 0xF;
ch2c++;
ch3f = freq(0xFF1D) * 100 / 32;
if(ch3f == 0)
ch3f = 1;
ch3s = 0;
if(mem[0xFF1A] & 0x80){
if(ch3c >= freq(0xFF1D))
ch3c = 0;
k = ch3c * 100 / ch3f;
ch3s = mem[0xFF30 + (k >> 1)];
if(k & 1)
ch3s &= 0xF;
else
ch3s >>= 4;
switch(mem[0xFF1C]){
case 0:
ch3s = 0;
break;
case 2:
ch3s >>= 1;
break;
case 3:
ch3s >>= 2;
break;
}
ch3s *= 8000 / 0xF;
ch3c++;
}
r = mem[0xFF22] & 7;
s = mem[0xFF22] >> 4;
if(r != 0)
ch4f = 524288 / r;
else
ch4f = 524288 * 2;
ch4f >>= s+1;
if(ch4f == 0)
ch4f = 1;
ch4f = SAMPLE / ch4f;
if(ch4c >= ch4f){
ch4sr <<= 1;
if(mem[0xFF22] & 4)
k = ((ch4sr >> 6) ^ (ch4sr >> 7)) & 1;
else
k = ((ch4sr >> 14) ^ (ch4sr >> 15)) & 1;
ch4sr |= k;
ch4c = 0;
}
ch4c++;
if(ch4sr & 1)
ch4s = -1;
else
ch4s = 1;
ch4s *= ch4v >> 4;
ch4s *= 8000 / 0xF;
smp[0] = 0;
smp[1] = 0;
f = mem[0xFF25];
r = mem[0xFF26] & 15;
r = r | (r << 4);
f &= r;
if(f & 0x01) smp[0] += ch1s;
if(f & 0x02) smp[0] += ch2s;
if(f & 0x04) smp[0] += ch3s;
if(f & 0x08) smp[0] += ch4s;
if(f & 0x10) smp[1] += ch1s;
if(f & 0x20) smp[1] += ch2s;
if(f & 0x40) smp[1] += ch3s;
if(f & 0x80) smp[1] += ch4s;
}
void
setpri(int pri)
{
char buf[64];
int fd;
snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
if((fd = open(buf, OWRITE)) >= 0){
fprint(fd, "pri %d\n", pri);
close(fd);
}
}
void
audioproc(void *)
{
short samples[10 * 2];
int i;
setpri(13);
for(;;){
if(paused)
memset(samples, 0, sizeof samples);
else
for(i = 0; i < sizeof samples/4; i++)
dosample(samples + 2 * i);
write(fd, samples, sizeof samples);
}
}
void
initaudio(void)
{
mem[0xFF26] = 0xF;
ch1v = 0xF0;
ch2v = 0xF0;
ch4v = 0xF0;
fd = open("/dev/audio", OWRITE);
if(fd < 0)
return;
proccreate(audioproc, nil, 8192);
}