package audio.gme;
// Nintendo SPC-700 DSP emulator
// http://www.slack.net/~ant/
/* Copyright (C) 2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
public final class SpcDsp
{
// Initializes DSP with new RAM and 128 bytes of register state (beginning at regs [regs_offset]).
// Keeps reference to ram_64k.
public void init( byte [] ram_64k, byte [] regs, int regs_offset )
{
this.ram = ram_64k;
for ( int i = register_count; --i >= 0; )
this.regs [i] = regs [i + regs_offset];
java.util.Arrays.fill( echo_hist, 0, echo_hist.length, 0 );
echo_hist_pos = 0;
every_other_sample = 1;
kon = 0;
lfsr = 0x4000;
echo_offset = 0;
echo_length = 0;
new_kon = this.regs [r_kon];
t_koff = 0;
// counters start out with this synchronization
counter0.i = 1;
counter1.i = 0;
counter2.i = -32;
counter3.i = 11;
// Internal state
for ( int i = voice_count; --i >= 0; )
{
Voice v = new Voice();
voices [i] = v;
v.brr_offset = 1;
}
}
// Sets output volume, where 1.0 is normal and 2.0 is twice as loud
public void setVolume( double v ) { volume = (int) (v * 0x8000); }
// Sets buffer to write samples into
public void setOutput( byte [] out )
{
this.out = out;
out_pos = 0;
}
// Number of samples written into buffer (stereo, so always a multiple of 2)
public int sampleCount() { return out_pos >> 1; }
// Writes to DSP register
public void write( int addr, int data )
{
if ( addr == r_endx ) // always cleared, regardless of data written
data = 0;
regs [addr] = (byte) data;
if ( addr == r_kon )
new_kon = (byte) data;
}
// DSP registers
static final int r_mvoll = 0x0C;
static final int r_mvolr = 0x1C;
static final int r_evoll = 0x2C;
static final int r_evolr = 0x3C;
static final int r_kon = 0x4C;
static final int r_koff = 0x5C;
static final int r_flg = 0x6C;
static final int r_endx = 0x7C;
static final int r_efb = 0x0D;
static final int r_pmon = 0x2D;
static final int r_non = 0x3D;
static final int r_eon = 0x4D;
static final int r_dir = 0x5D;
static final int r_esa = 0x6D;
static final int r_edl = 0x7D;
static final int r_fir = 0x0F;
// Voice registers
static final int v_voll = 0x00;
static final int v_volr = 0x01;
static final int v_pitchl = 0x02;
static final int v_pitchh = 0x03;
static final int v_srcn = 0x04;
static final int v_adsr0 = 0x05;
static final int v_adsr1 = 0x06;
static final int v_gain = 0x07;
static final int v_envx = 0x08;
static final int v_outx = 0x09;
public static final int register_count = 128;
public final byte [] regs = new byte [register_count];
// Runs DSP for sampleCount/32000 of a second
public void run( int sampleCount )
{
// locals are faster, and first three are more efficient to access
final byte [] regs = this.regs;
Voice v;
final byte [] ram = this.ram;
final Rate [] rates = this.rates;
final Voice [] voices = this.voices;
final int flg = regs [r_flg];
final int dir = (regs [r_dir] & 0xFF) << 8;
final int slow_gaussian = ((regs [r_pmon] & 0xFF) >> 1) | regs [r_non];
final Rate noise_rate = rates [flg & 0x1F];
// Global volumes
final int volume = (flg & 0x40) == 0 ? this.volume : 0;
final int mvoll = (regs [r_mvoll] * volume) >> 15;
final int mvolr = (regs [r_mvolr] * volume) >> 15;
final int evoll = (regs [r_evoll] * volume) >> 15;
final int evolr = (regs [r_evolr] * volume) >> 15;
final byte [] out = this.out;
int out_pos = this.out_pos;
final int out_end = out_pos + (sampleCount << 2);
do
{
// KON/KOFF reading
if ( (every_other_sample ^= 1) != 0 )
{
kon = (new_kon &= ~kon);
t_koff = regs [r_koff];
}
// run counters
{ int n = counter1.i; if ( (n & 7) == 0 ) n -= 6 - 1; counter1.i = n - 1; }
{ int n = counter2.i; if ( (n & 7) == 0 ) n -= 6 - 2; counter2.i = n - 1; }
{ int n = counter3.i; if ( (n & 7) == 0 ) n -= 6 - 3; counter3.i = n - 1; }
// Noise
if ( (noise_rate.c.i & noise_rate.m) == 0 )
lfsr = (lfsr >> 1) ^ (-(lfsr & 2) & 0xC000);
// Voices
int pmon_input = 0;
int main_out_l = 0;
int main_out_r = 0;
int echo_out_l = 0;
int echo_out_r = 0;
int voice = -1;
do
{
v = voices [++voice];
final int vbit = 1 << voice;
final int v_regs = voice << 4;
// Pitch
int pitch = (regs [v_regs + v_pitchh] & 0x3F) << 8 | (regs [v_regs + v_pitchl] & 0xFF);
if ( (regs [r_pmon] & vbit) != 0 )
pitch += ((pmon_input >> 5) * pitch) >> 10;
int brr_header = ram [v.brr_addr];
// KON phases
if ( v.kon_delay > 0 )
{
final int kon_delay = --v.kon_delay;
// Disable BRR decoding until last three samples
v.interp_pos = (kon_delay & 3) != 0 ? 0x4000 : 0;
// Get ready to start BRR decoding on next sample
if ( kon_delay == 4 )
{
int addr = dir + ((regs [v_regs + v_srcn] & 0xFF) << 2);
v.brr_addr = (ram [addr + 1] & 0xFF) << 8 | (ram [addr] & 0xFF);
v.brr_offset = 1;
v.buf_pos = 0;
brr_header = 0; // header is ignored on this sample
}
// Envelope is never run during KON
v.env = 0;
v.hidden_env = 0;
// Pitch is never added during KON
pitch = 0;
}
int env;
regs [v_regs + v_envx] = (byte) ((env = v.env) >> 4);
// Gaussian interpolation
{
int output = 0;
if ( env != 0 )
{
int whole = v.buf_pos + (v.interp_pos >> 12);
int fract = v.interp_pos >> 3 & 0x1FE;
if ( (slow_gaussian & vbit) == 0 ) // 99%
{
// Faster approximation when exact sample value isn't necessary for pitch mod
output = (((gauss [fract ] * v.buf [whole ] +
gauss [fract+1 ] * v.buf [whole+1] +
gauss [511-fract] * v.buf [whole+2] +
gauss [510-fract] * v.buf [whole+3]) >> 11) * env) >> 11;
}
else
{
output = (short) (lfsr << 1);
if ( (regs [r_non] & vbit) == 0 )
{
output = (gauss [fract ] * v.buf [whole ]) >> 11;
output += (gauss [fract+1 ] * v.buf [whole+1]) >> 11;
output += (gauss [511-fract] * v.buf [whole+2]) >> 11;
output = (short) output;
output += (gauss [510-fract] * v.buf [whole+3]) >> 11;
if ( (short) output != output ) output = (output >> 24) ^ 0x7FFF; // 16-bit clamp
output &= ~1;
}
pmon_input = output = (output * env) >> 11 & ~1;
}
regs [v_regs + v_outx] = (byte) (output >> 8);
// Output
int l, r;
main_out_l += (l = output * regs [v_regs + v_voll]);
main_out_r += (r = output * regs [v_regs + v_volr]);
if ( (regs [r_eon] & vbit) != 0 )
{
echo_out_l += l;
echo_out_r += r;
}
}
}
// Soft reset or end of sample
if ( flg < 0 || (brr_header & 3) == 1 )
{
v.env_mode = env_release;
env = 0;
}
if ( every_other_sample != 0 )
{
// KOFF
if ( (t_koff & vbit) != 0 )
v.env_mode = env_release;
// KON
if ( (kon & vbit) != 0 )
{
v.kon_delay = 5;
v.env_mode = env_attack;
regs [r_endx] &= ~vbit;
}
}
// Envelope
if ( v.kon_delay == 0 )
{
if ( v.env_mode == env_release ) // 97%
{
if ( (v.env = (env -= 0x8)) <= 0 )
{
v.env = 0;
continue; // no BRR decoding for you!
}
}
else do // 3%
{
int rate;
int env_data = regs [v_regs + v_adsr1] & 0xFF;
int adsr0;
if ( (adsr0 = regs [v_regs + v_adsr0]) < 0 ) // 97% ADSR
{
if ( v.env_mode > env_decay ) // 89%
{
// optimized handling
v.hidden_env = (env -= (env >> 8) + 1);
Rate r = rates [env_data & 0x1F];
if ( (r.c.i & r.m) == 0 )
v.env = env;
break;
}
else if ( v.env_mode == env_decay )
{
env -= (env >> 8) + 1;
rate = (adsr0 >> 3 & 0x0E) + 0x10;
}
else // env_attack
{
rate = ((adsr0 & 0x0F) << 1) + 1;
env += rate < 31 ? 0x20 : 0x400;
}
}
else // GAIN
{
int mode;
env_data = regs [v_regs + v_gain] & 0xFF;
mode = env_data >> 5;
if ( mode < 4 ) // direct
{
env = env_data << 4;
rate = 31;
}
else
{
rate = env_data & 0x1F;
if ( mode == 4 ) // 4: linear decrease
{
env -= 0x20;
}
else if ( mode < 6 ) // 5: exponential decrease
{
env -= (env >> 8) + 1;
}
else // 6,7: linear increase
{
env += 0x20;
if ( mode > 6 && (v.hidden_env < 0 || v.hidden_env >= 0x600) )
env += 0x8 - 0x20; // 7: two-slope linear increase
}
}
}
// Sustain level
if ( (env >> 8) == (env_data >> 5) && v.env_mode == env_decay )
v.env_mode = env_sustain;
v.hidden_env = env;
if ( env < 0 || env > 0x7FF )
{
env = (env < 0 ? 0 : 0x7FF);
if ( v.env_mode == env_attack )
v.env_mode = env_decay;
}
Rate r = rates [rate];
if ( (r.c.i & r.m) == 0 )
v.env = env; // nothing else is controlled by the counter
}
while ( false );
}
// Apply pitch
int old_pos;
int interp_pos = ((old_pos = v.interp_pos) & 0x3FFF) + pitch;
if ( interp_pos > 0x7FFF )
interp_pos = 0x7FFF;
v.interp_pos = interp_pos;
// BRR decode if necessary
if ( old_pos > 0x4000 - 1 )
{
// Arrange the four input nybbles in 0xABCD order for easy decoding
int brr_addr = v.brr_addr;
int brr_offset = v.brr_offset;
int nybbles = ram [brr_addr + brr_offset] << 8 | (ram [brr_addr + brr_offset + 1] & 0xFF);
// Advance read position
final int brr_block_size = 9;
if ( (brr_offset += 2) >= brr_block_size )
{
// Next BRR block
brr_addr = (brr_addr + brr_block_size) & 0xFFFF;
//assert brr_offset == brr_block_size;
if ( (brr_header & 1) != 0 )
{
int addr = dir + ((regs [v_regs + v_srcn] & 0xFF) << 2);
brr_addr = (ram [addr + 3] & 0xFF) << 8 | (ram [addr + 2] & 0xFF);
if ( v.kon_delay == 0 )
regs [r_endx] |= vbit;
}
v.brr_addr = brr_addr;
brr_offset = 1;
}
v.brr_offset = brr_offset;
// Decode
final int scale = brr_header >> 4 & 0x0F;
final int right_shift = brr_shifts [scale];
final int left_shift = brr_shifts [scale + 16];
final int filter = brr_header & 0x0C;
// Decode and write to next four samples in circular buffer
int pos = v.buf_pos;
int p1 = v.buf [pos + (brr_buf_size - 1)];
int p2 = v.buf [pos + (brr_buf_size - 2)] >> 1;
final int end = pos + 4;
do
{
// Extract upper nybble and scale appropriately
int s = ((short) nybbles >> right_shift) << left_shift;
nybbles <<= 4;
// Apply IIR filter (8 is the most commonly used)
if ( filter >= 8 )
{
if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875
s += p1 - p2 + (p2 >> 4) + ((p1 * -3) >> 6);
else // s += p1 * 0.8984375 - p2 * 0.40625
s += p1 - p2 + ((p1 * -13) >> 7) + ((p2 * 3) >> 4);
}
else if ( filter != 0 ) // s += p1 * 0.46875
{
s += (p1 >> 1) + ((-p1) >> 5);
}
p2 = p1 >> 1;
// Adjust and write sample
if ( (short) s != s ) s = (s >> 24) ^ 0x7FFF; // 16-bit clamp
v.buf [pos + brr_buf_size] = v.buf [pos] = p1 = (short) (s << 1);
// second copy simplifies wrap-around
}
while ( ++pos < end );
if ( pos >= brr_buf_size )
pos = 0;
v.buf_pos = pos;
}
}
while ( voice < 7 );
// Echo position
int echo_offset;
int echo_ptr = ((regs [r_esa] << 8) + (echo_offset = this.echo_offset)) & 0xFFFF;
if ( echo_offset == 0 )
echo_length = (regs [r_edl] & 0x0F) << 11;
if ( (echo_offset += 4) >= echo_length )
echo_offset = 0;
this.echo_offset = echo_offset;
// FIR
int echo_hist_pos;
this.echo_hist_pos = echo_hist_pos = (this.echo_hist_pos + 2) & (echo_hist_half - 1);
int echo_in_l = ram [echo_ptr + 1] << 8 | (ram [echo_ptr ] & 0xFF);
echo_hist [echo_hist_pos ] = echo_hist [echo_hist_pos + echo_hist_half] = echo_in_l;
int echo_in_r = ram [echo_ptr + 3] << 8 | (ram [echo_ptr + 2] & 0xFF);
echo_hist [echo_hist_pos + 1] = echo_hist [echo_hist_pos + echo_hist_half + 1] = echo_in_r;
echo_in_l = regs [r_fir + 0x70] * echo_in_l +
regs [r_fir ] * echo_hist [echo_hist_pos + 2] +
regs [r_fir + 0x10] * echo_hist [echo_hist_pos + 4] +
regs [r_fir + 0x20] * echo_hist [echo_hist_pos + 6] +
regs [r_fir + 0x30] * echo_hist [echo_hist_pos + 8] +
regs [r_fir + 0x40] * echo_hist [echo_hist_pos + 10] +
regs [r_fir + 0x50] * echo_hist [echo_hist_pos + 12] +
regs [r_fir + 0x60] * echo_hist [echo_hist_pos + 14];
echo_in_r = regs [r_fir + 0x70] * echo_in_r +
regs [r_fir ] * echo_hist [echo_hist_pos + 3] +
regs [r_fir + 0x10] * echo_hist [echo_hist_pos + 5] +
regs [r_fir + 0x20] * echo_hist [echo_hist_pos + 7] +
regs [r_fir + 0x30] * echo_hist [echo_hist_pos + 9] +
regs [r_fir + 0x40] * echo_hist [echo_hist_pos + 11] +
regs [r_fir + 0x50] * echo_hist [echo_hist_pos + 13] +
regs [r_fir + 0x60] * echo_hist [echo_hist_pos + 15];
// Echo out
if ( (flg & 0x20) == 0 )
{
final int efb = regs [r_efb];
int l = (echo_out_l >> 7) + ((echo_in_l * efb) >> 14);
if ( (short) l != l ) l = (l >> 24) ^ 0x7FFF; // 16-bit clamp
ram [echo_ptr ] = (byte) l;
ram [echo_ptr + 1] = (byte) (l >> 8);
int r = (echo_out_r >> 7) + ((echo_in_r * efb) >> 14);
if ( (short) r != r ) r = (r >> 24) ^ 0x7FFF; // 16-bit clamp
ram [echo_ptr + 2] = (byte) r;
ram [echo_ptr + 3] = (byte) (r >> 8);
}
// Sound out
int l = (main_out_l * mvoll + echo_in_l * evoll) >> 14;
if ( (short) l != l ) l = (l >> 24) ^ 0x7FFF; // 16-bit clamp
out [out_pos ] = (byte) (l >> 8);
out [out_pos + 1] = (byte) l;
int r = (main_out_r * mvolr + echo_in_r * evolr) >> 14;
if ( (short) r != r ) r = (r >> 24) ^ 0x7FFF; // 16-bit clamp
out [out_pos + 2] = (byte) (r >> 8);
out [out_pos + 3] = (byte) r;
}
while ( (out_pos += 4) < out_end );
this.out_pos = out_pos;
}
public SpcDsp()
{
int mask = 4095;
for ( int i = 0; i < 32 - 2; i += 3 )
{
rates [i ] = new Rate( counter2, mask );
rates [i+1] = new Rate( counter1, mask );
rates [i+2] = new Rate( counter3, mask );
mask >>= 1;
}
rates [ 0] = new Rate( counter0, 7 );
rates [30] = new Rate( counter2, 1 );
rates [31] = new Rate( counter2, 0 );
setVolume( 2.0 ); // TODO: let line increase volume, not DSP
}
static final int env_release = 0;
static final int env_attack = 1;
static final int env_decay = 2;
static final int env_sustain = 3;
static final int voice_count = 8;
static final int brr_buf_size = 12;
static final int echo_hist_half = 16;
private static final class Voice
{
final int [] buf = new int [12*2];// decoded samples (twice the size to simplify wrap handling)
int buf_pos; // place in buffer where next samples will be decoded
int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
int brr_addr; // address of current BRR block
int brr_offset; // current decoding offset in BRR block
int kon_delay; // KON delay/current setup phase
int env_mode;
int env; // current envelope level
int hidden_env; // used by GAIN mode 7, very obscure quirk
}
private static final class Counter { int i; }
private static final class Rate
{
Counter c;
int m;
Rate( Counter c, int m )
{
this.c = c;
this.m = m;
}
}
final Counter counter0 = new Counter();
final Counter counter1 = new Counter();
final Counter counter2 = new Counter();
final Counter counter3 = new Counter();
final Rate [] rates = new Rate [32];
final Voice [] voices = new Voice [voice_count];
final int [] echo_hist = new int [echo_hist_half * 2];
int echo_hist_pos;
int every_other_sample; // toggles every sample
int kon; // KON value when last checked
int lfsr;
int echo_offset; // offset from ESA in echo buffer
int echo_length; // number of bytes that echo_offset will stop at
int new_kon;
int t_koff;
int volume;
byte [] ram; // 64K shared RAM between DSP and SMP
byte [] out; // sample output
int out_pos;
// 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11
static final int [] brr_shifts = {
13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11
};
static final int [] gauss =
{
370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303,
339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299,
311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292,
283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282,
257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269,
233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253,
210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234,
188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213,
168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190,
150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164,
132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136,
117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106,
102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074,
89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040,
77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005,
66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969,
56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932,
48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894,
40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855,
33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816,
27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777,
22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737,
17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698,
14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659,
10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620,
8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582,
5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545,
4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508,
2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473,
1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439,
0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405,
0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374,
};
}