shithub: riscv

ref: 7ceff03db37d98e1dc634198fcee4206d8318499
dir: /sys/src/games/gb/audio.c/

View raw version
#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);
}