rockbox/apps/plugins/rockboy/sound.c

768 lines
18 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* The following code is rewrite of the C++ code provided in VisualBoyAdvance.
* There are also portions of the original GNUboy code.
* Copyright (C) 2001 the GNUboy development team
*
* VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
* Copyright (C) 1999-2003 Forgotten
* Copyright (C) 2004 Forgotten and the VBA development team
*
* VisualBoyAdvance conversion from C++ to C by Karl Kurbjun July, 2007
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
*/
#include "rockmacros.h"
#include "defs.h"
#include "pcm.h"
#include "sound.h"
#include "cpu-gb.h"
#include "hw.h"
#include "regs.h"
static const byte soundWavePattern[4][32] = {
{0x01,0x01,0x01,0x01,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff},
{0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff},
{0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff},
{0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff}
};
int soundFreqRatio[8] ICONST_ATTR= {
1048576, // 0
524288, // 1
262144, // 2
174763, // 3
131072, // 4
104858, // 5
87381, // 6
74898 // 7
};
int soundShiftClock[16] ICONST_ATTR= {
2, // 0
4, // 1
8, // 2
16, // 3
32, // 4
64, // 5
128, // 6
256, // 7
512, // 8
1024, // 9
2048, // 10
4096, // 11
8192, // 12
16384, // 13
1, // 14
1 // 15
};
struct snd snd IBSS_ATTR;
#define RATE (snd.rate)
#define WAVE (ram.hi+0x30)
#define S1 (snd.ch[0])
#define S2 (snd.ch[1])
#define S3 (snd.ch[2])
#define S4 (snd.ch[3])
#define SOUND_MAGIC 0x60000000
#define SOUND_MAGIC_2 0x30000000
#define NOISE_MAGIC 5
static void gbSoundChannel1(int *r, int *l)
{
int vol = S1.envol;
int freq = 0;
int value = 0;
if(S1.on && (S1.len || !S1.cont))
{
S1.pos += snd.quality*S1.skip;
S1.pos &= 0x1fffffff;
value = ((signed char)S1.wave[S1.pos>>24]) * vol;
}
if (snd.balance & 1) *r += value;
if (snd.balance & 16) *l += value;
if(S1.on)
{
if(S1.len)
{
S1.len-=snd.quality;
if(S1.len <=0 && S1.cont)
{
R_NR52 &= 0xfe;
S1.on = 0;
}
}
if(S1.enlen)
{
S1.enlen-=snd.quality;
if(S1.enlen<=0)
{
if(S1.endir)
{
if(S1.envol < 15)
S1.envol++;
}
else
{
if(S1.envol)
S1.envol--;
}
S1.enlen += S1.enlenreload;
}
}
if(S1.swlen)
{
S1.swlen-=snd.quality;
if(S1.swlen<=0)
{
freq = (((int)(R_NR14&7) << 8) | R_NR13);
int updown = 1;
if(S1.swdir)
updown = -1;
int newfreq = 0;
if(S1.swsteps)
{
newfreq = freq + updown * freq / BIT_N(S1.swsteps);
if(newfreq == freq)
newfreq = 0;
}
else
newfreq = freq;
if(newfreq < 0)
{
S1.swlen += S1.swlenreload;
}
else if(newfreq > 2047)
{
S1.swlen = 0;
S1.on = 0;
R_NR52 &= 0xfe;
}
else
{
S1.swlen += S1.swlenreload;
S1.skip = SOUND_MAGIC/(2048 - newfreq);
R_NR13 = newfreq & 0xff;
R_NR14 = (R_NR14 & 0xf8) |((newfreq >> 8) & 7);
}
}
}
}
}
static void gbSoundChannel2(int *r, int *l)
{
int vol = S2.envol;
int value = 0;
if(S2.on && (S2.len || !S2.cont))
{
S2.pos += snd.quality*S2.skip;
S2.pos &= 0x1fffffff;
value = ((signed char)S2.wave[S2.pos>>24]) * vol;
}
if (snd.balance & 2) *r += value;
if (snd.balance & 32) *l += value;
if(S2.on) {
if(S2.len) {
S2.len-=snd.quality;
if(S2.len <= 0 && S2.cont) {
R_NR52 &= 0xfd;
S2.on = 0;
}
}
if(S2.enlen) {
S2.enlen-=snd.quality;
if(S2.enlen <= 0) {
if(S2.endir) {
if(S2.envol < 15)
S2.envol++;
} else {
if(S2.envol)
S2.envol--;
}
S2.enlen += S2.enlenreload;
}
}
}
}
static void gbSoundChannel3(int *r, int *l)
{
int s;
if (S3.on && (S3.len || !S3.cont))
{
S3.pos += S3.skip*snd.quality;
S3.pos &= 0x1fffffff;
s=ram.hi[0x30 + (S3.pos>>25)];
if (S3.pos & 0x01000000)
s &= 0x0f;
else
s >>= 4;
s -= 8;
switch(S3.outputlevel)
{
case 0:
s=0;
break;
case 1:
break;
case 2:
s=s>>1;
break;
case 3:
s=s>>2;
break;
}
if (snd.balance & 4) *r += s;
if (snd.balance & 64) *l += s;
}
if(S3.on)
{
if(S3.len)
{
S3.len-=snd.quality;
if(S3.len<=0 && S3.cont)
{
R_NR52 &= 0xFB;
S3.on=0;
}
}
}
}
static void gbSoundChannel4(int *r, int *l)
{
int vol = S4.envol;
int value = 0;
if(S4.clock <= 0x0c)
{
if(S4.on && (S4.len || !S4.cont))
{
S4.pos += snd.quality*S4.skip;
S4.shiftpos += snd.quality*S4.shiftskip;
if(S4.nsteps)
{
while(S4.shiftpos > 0x1fffff) {
S4.shiftright = (((S4.shiftright << 6) ^
(S4.shiftright << 5)) & 0x40) | (S4.shiftright >> 1);
S4.shiftpos -= 0x200000;
}
}
else
{
while(S4.shiftpos > 0x1fffff)
{
S4.shiftright = (((S4.shiftright << 14) ^
(S4.shiftright << 13)) & 0x4000) | (S4.shiftright >> 1);
S4.shiftpos -= 0x200000;
}
}
S4.pos &= 0x1fffff;
S4.shiftpos &= 0x1fffff;
value = ((S4.shiftright & 1)*2-1) * vol;
}
else
{
value = 0;
}
}
if (snd.balance & 8) *r += value;
if (snd.balance & 128) *l += value;
if(S4.on) {
if(S4.len) {
S4.len-=snd.quality;
if(S4.len <= 0 && S4.cont) {
R_NR52 &= 0xfd;
S4.on = 0;
}
}
if(S4.enlen) {
S4.enlen-=snd.quality;
if(S4.enlen <= 0)
{
if(S4.endir)
{
if(S4.envol < 15)
S4.envol++;
}
else
{
if(S4.envol)
S4.envol--;
}
S4.enlen += S4.enlenreload;
}
}
}
}
void sound_mix(void)
{
int l, r;
if (!RATE || cpu.snd < RATE) return;
for (; cpu.snd >= RATE; cpu.snd -= RATE)
{
l = r = 0;
gbSoundChannel1(&r,&l);
gbSoundChannel2(&r,&l);
gbSoundChannel3(&r,&l);
gbSoundChannel4(&r,&l);
if(snd.gbDigitalSound)
{
l = snd.level1<<8;
r = snd.level2<<8;
}
else
{
l *= snd.level1*60;
r *= snd.level2*60;
}
if(l > 32767)
l = 32767;
if(l < -32768)
l = -32768;
if(r > 32767)
r = 32767;
if(r < -32768)
r = -32768;
if (pcm.buf)
{
if (pcm.pos >= pcm.len)
pcm_submit();
if (pcm.stereo)
{
pcm.buf[pcm.pos++] = l;
pcm.buf[pcm.pos++] = r;
}
else pcm.buf[pcm.pos++] = ((l+r)>>1);
}
}
R_NR52 = (R_NR52&0xf0) | S1.on | (S2.on<<1) | (S3.on<<2) | (S4.on<<3);
}
byte sound_read(byte r)
{
if(!options.sound) return 0;
sound_mix();
/* printf("read %02X: %02X\n", r, REG(r)); */
return REG(r);
}
void sound_write(byte r, byte b)
{
int freq=0;
ram.hi[r]=b;
if(!options.sound)
return;
sound_mix();
switch (r)
{
case RI_NR10:
S1.swlen = S1.swlenreload = 344 * ((b >> 4) & 7);
S1.swsteps = b & 7;
S1.swdir = b & 0x08;
S1.swstep = 0;
break;
case RI_NR11:
S1.len = 172 * (64 - (b & 0x3f));
S1.wave = soundWavePattern[b >> 6];
break;
case RI_NR12:
S1.envol = b >> 4;
S1.endir = b & 0x08;
S1.enlenreload = S1.enlen = 689 * (b & 7);
break;
case RI_NR13:
freq = (((int)(R_NR14 & 7)) << 8) | b;
S1.len = 172 * (64 - (R_NR11 & 0x3f));
freq = 2048 - freq;
if(freq)
{
S1.skip = SOUND_MAGIC / freq;
}
else
{
S1.skip = 0;
}
break;
case RI_NR14:
freq = (((int)(b&7) << 8) | R_NR13);
freq = 2048 - freq;
S1.len = 172 * (64 - (R_NR11 & 0x3f));
S1.cont = b & 0x40;
if(freq)
{
S1.skip = SOUND_MAGIC / freq;
}
else
{
S1.skip = 0;
}
if(b & 0x80)
{
R_NR52 |= 1;
S1.envol = R_NR12 >> 4;
S1.endir = R_NR12 & 0x08;
S1.len = 172 * (64 - (R_NR11 & 0x3f));
S1.enlenreload = S1.enlen = 689 * (R_NR12 & 7);
S1.swlen = S1.swlenreload = 344 * ((R_NR10 >> 4) & 7);
S1.swsteps = R_NR10 & 7;
S1.swdir = R_NR10 & 0x08;
S1.swstep = 0;
S1.pos = 0;
S1.on = 1;
}
break;
case RI_NR21:
S2.wave = soundWavePattern[b >> 6];
S2.len = 172 * (64 - (b & 0x3f));
break;
case RI_NR22:
S2.envol = b >> 4;
S2.endir = b & 0x08;
S2.enlenreload = S2.enlen = 689 * (b & 7);
break;
case RI_NR23:
freq = (((int)(R_NR24 & 7)) << 8) | b;
S2.len = 172 * (64 - (R_NR21 & 0x3f));
freq = 2048 - freq;
if(freq)
{
S2.skip = SOUND_MAGIC / freq;
}
else
{
S2.skip = 0;
}
break;
case RI_NR24:
freq = (((int)(b&7) << 8) | R_NR23);
freq = 2048 - freq;
S2.len = 172 * (64 - (R_NR21 & 0x3f));
S2.cont = b & 0x40;
if(freq) {
S2.skip = SOUND_MAGIC / freq;
} else
S2.skip = 0;
if(b & 0x80) {
R_NR52 |= 2;
S2.envol = R_NR22 >> 4;
S2.endir = R_NR22 & 0x08;
S2.len = 172 * (64 - (R_NR21 & 0x3f));
S2.enlenreload = S2.enlen = 689 * (R_NR22 & 7);
S2.pos = 0;
S2.on = 1;
}
break;
case RI_NR30:
if (!(b & 0x80)){
R_NR52 &= 0xfb;
S3.on = 0;
}
break;
case RI_NR31:
S3.len = (256-R_NR31) * 172;
break;
case RI_NR32:
S3.outputlevel = (b >> 5) & 3;
break;
case RI_NR33:
freq = 2048 - (((int)(R_NR34&7) << 8) | b);
if(freq)
S3.skip = SOUND_MAGIC_2 / freq;
else
S3.skip = 0;
break;
case RI_NR34:
freq = 2048 - (((b&7)<<8) | R_NR33);
if(freq)
S3.skip = SOUND_MAGIC_2 / freq;
else
S3.skip = 0;
S3.cont=b & 0x40;
if((b & 0x80) && (R_NR30 & 0x80))
{
R_NR52 |= 4;
S3.len = 172 * (256 - R_NR31);
S3.pos = 0;
S3.on = 1;
}
break;
case RI_NR41:
S4.len = 172 * (64 - (b & 0x3f));
break;
case RI_NR42:
S4.envol = b >> 4;
S4.endir = b & 0x08;
S4.enlenreload = S4.enlen = 689 * (b & 7);
break;
case RI_NR43:
freq = soundFreqRatio[b & 7];
S4.nsteps = b & 0x08;
S4.skip = (freq << 8) / NOISE_MAGIC;
S4.clock = b >> 4;
freq = freq / soundShiftClock[S4.clock];
S4.shiftskip = (freq << 8) / NOISE_MAGIC;
break;
case RI_NR44:
S4.cont = b & 0x40;
if(b & 0x80)
{
R_NR52 |= 8;
S4.envol = R_NR42 >> 4;
S4.endir = R_NR42 & 0x08;
S4.len = 172 * (64 - (R_NR41 & 0x3f));
S4.enlenreload = S4.enlen = 689 * (R_NR42 & 7);
S4.on = 1;
S4.pos = 0;
S4.shiftpos = 0;
freq = soundFreqRatio[R_NR43 & 7];
S4.shiftpos = (freq << 8) / NOISE_MAGIC;
S4.nsteps = R_NR43 & 0x08;
freq = freq / soundShiftClock[R_NR43 >> 4];
S4.shiftskip = (freq << 8) / NOISE_MAGIC;
if(S4.nsteps)
{
S4.shiftright = 0x7fff;
}
else
{
S4.shiftright = 0x7f;
}
}
break;
case RI_NR50:
snd.level1 = b & 7;
snd.level2 = (b >> 4) & 7;
break;
case RI_NR51:
snd.balance = b;
break;
case RI_NR52:
if (!(b & 0x80))
{
S1.on=0;
S2.on=0;
S3.on=0;
S4.on=0;
}
break;
}
snd.gbDigitalSound = true;
if(S1.on && S1.envol != 0)
snd.gbDigitalSound = false;
if(S2.on && S2.envol != 0)
snd.gbDigitalSound = false;
if(S3.on && S3.outputlevel != 0)
snd.gbDigitalSound = false;
if(S4.on && S4.envol != 0)
snd.gbDigitalSound = false;
}
void sound_reset(void)
{
snd.level1 = 7;
snd.level2 = 7;
S1.on = S2.on = S3.on = S4.on = 0;
S1.len = S2.len = S3.len = S4.len = 0;
S1.skip = S2.skip = S3.skip = S4.skip = 0;
S1.pos = S2.pos = S3.pos = S4.pos = 0;
S1.cont = S2.cont = S3.cont = S4.cont = 0;
S1.envol = S2.envol = S4.envol = 0;
S1.enlen = S2.enlen = S4.enlen = 0;
S1.endir = S2.endir = S4.endir = 0;
S1.enlenreload = S2.enlenreload = S4.enlenreload = 0;
S1.swlen = 0;
S1.swlenreload = 0;
S1.swsteps = 0;
S1.swdir = 0;
S1.swstep = 0;
S1.wave = S2.wave = soundWavePattern[2];
S3.outputlevel = 0;
S4.clock = 0;
S4.shiftright = 0x7f;
S4.nsteps = 0;
sound_write(0x10, 0x80);
sound_write(0x11, 0xbf);
sound_write(0x12, 0xf3);
sound_write(0x14, 0xbf);
sound_write(0x16, 0x3f);
sound_write(0x17, 0x00);
sound_write(0x19, 0xbf);
sound_write(0x1a, 0x7f);
sound_write(0x1b, 0xff);
sound_write(0x1c, 0xbf);
sound_write(0x1e, 0xbf);
sound_write(0x20, 0xff);
sound_write(0x21, 0x00);
sound_write(0x22, 0x00);
sound_write(0x23, 0xbf);
sound_write(0x24, 0x77);
sound_write(0x25, 0xf3);
sound_write(0x26, 0xf0);
S1.on = 0;
S2.on = 0;
S3.on = 0;
S4.on = 0;
int addr = 0x30;
while(addr < 0x40)
{
ram.hi[addr++] = 0x00;
ram.hi[addr++] = 0xff;
}
if (pcm.hz)
{
snd.rate = (1<<21) / pcm.hz;
snd.quality=44100 / pcm.hz;
}
else snd.rate = 0;
}
void sound_dirty(void)
{
sound_write(RI_NR10, R_NR10);
sound_write(RI_NR11, R_NR11);
sound_write(RI_NR12, R_NR12);
sound_write(RI_NR13, R_NR13);
sound_write(RI_NR14, R_NR14);
sound_write(RI_NR21, R_NR21);
sound_write(RI_NR22, R_NR22);
sound_write(RI_NR23, R_NR23);
sound_write(RI_NR24, R_NR24);
sound_write(RI_NR30, R_NR30);
sound_write(RI_NR31, R_NR31);
sound_write(RI_NR32, R_NR32);
sound_write(RI_NR33, R_NR33);
sound_write(RI_NR34, R_NR34);
sound_write(RI_NR42, R_NR42);
sound_write(RI_NR43, R_NR43);
sound_write(RI_NR44, R_NR44);
sound_write(RI_NR50, R_NR50);
sound_write(RI_NR51, R_NR51);
sound_write(RI_NR52, R_NR52);
}