rockbox/apps/plugins/rockboy/sound.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

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 / (1 << 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);
}