Mercurial > audlegacy
diff Plugins/Input/console/Spc_Dsp.cpp @ 90:252843aac42f trunk
[svn] Import the initial sources for console music support.
author | nenolod |
---|---|
date | Tue, 01 Nov 2005 19:57:26 -0800 |
parents | |
children | 7c5e886205ef |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/console/Spc_Dsp.cpp Tue Nov 01 19:57:26 2005 -0800 @@ -0,0 +1,644 @@ + +// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/ + +// Based on Brad Martin's OpenSPC DSP emulator. + +#include "Spc_Dsp.h" + +#include <string.h> + +#include "blargg_endian.h" + +/* Copyright (C) 2002 Brad Martin */ +/* Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include BLARGG_SOURCE_BEGIN + +Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ ) +{ + voices_muted = 0; + set_gain( 1.0 ); + + BOOST_STATIC_ASSERT( sizeof (g) == register_count && sizeof (voice) == register_count ); +} + +void Spc_Dsp::reset() +{ + keys = 0; + echo_ptr = 0; + noise_count = 0; + noise = 1; + fir_offset = 0; + + g.flags = 0xE0; // reset, mute, echo off + g.key_ons = 0; + + for ( int i = 0; i < voice_count; i++ ) { + voice_state [i].on_cnt = 0; + voice_state [i].envstate = state_release; + } + + memset( fir_buf, 0, sizeof fir_buf ); + memset( voice_vol, 0, sizeof voice_vol ); +} + +void Spc_Dsp::write( int i, int data ) +{ + require( (unsigned) i < register_count ); + + reg [i] = data; + int high = i >> 4; + switch ( i & 0x0f ) + { + // voice volume + case 0: + case 1: { + int left = (int8_t) reg [i & ~1]; + int right = (int8_t) reg [i | 1]; + voice_vol [high] [0] = left; + voice_vol [high] [1] = right; + // kill surround only if signs of volumes differ + if ( left * right < 0 ) + { + if ( left < 0 ) + voice_vol [high] [0] = -left; + else + voice_vol [high] [1] = -right; + } + break; + } + + // fir coefficients + case 0x0f: + fir_coeff [high] = (int8_t) data; // sign-extend + break; + } +} + +// This table is for envelope timing. It represents the number of counts +// that should be subtracted from the counter each sample period (32kHz). +// The counter starts at 30720 (0x7800). Each count divides exactly into +// 0x7800 without remainder. +const int env_rate_init = 0x7800; +static const short env_rates [0x20] = { + 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, + 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, + 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, + 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 +}; + +const int env_range = 0x800; + +int Spc_Dsp::clock_envelope( int v ) +{ /* Return value is current + * ENVX */ + raw_voice_t& raw_voice = this->voice [v]; + voice_t& voice = voice_state [v]; + + int envx = voice.envx; + if ( voice.envstate == state_release ) + { + /* + * Docs: "When in the state of "key off". the "click" sound is + * prevented by the addition of the fixed value 1/256" WTF??? + * Alright, I'm going to choose to interpret that this way: + * When a note is keyed off, start the RELEASE state, which + * subtracts 1/256th each sample period (32kHz). Note there's + * no need for a count because it always happens every update. + */ + envx -= env_range / 256; + if ( envx <= 0 ) { + envx = 0; + keys &= ~(1 << v); + return -1; + } + voice.envx = envx; + raw_voice.envx = envx >> 8; + return envx; + } + + int cnt = voice.envcnt; + int adsr1 = raw_voice.adsr [0]; + if ( adsr1 & 0x80 ) + { + switch ( voice.envstate ) + { + case state_attack: { + // increase envelope by 1/64 each step + int t = adsr1 & 15; + if ( t == 15 ) { + envx += env_range / 2; + } + else { + cnt -= env_rates [t * 2 + 1]; + if ( cnt > 0 ) + break; + envx += env_range / 64; + cnt = env_rate_init; + } + if ( envx >= env_range ) { + envx = env_range - 1; + voice.envstate = state_decay; + } + voice.envx = envx; + break; + } + + case state_decay: { + // Docs: "DR... [is multiplied] by the fixed value + // 1-1/256." Well, at least that makes some sense. + // Multiplying ENVX by 255/256 every time DECAY is + // updated. + cnt -= env_rates [((adsr1 >> 3) & 0xE) + 0x10]; + if ( cnt <= 0 ) { + cnt = env_rate_init; + envx -= ((envx - 1) >> 8) + 1; + voice.envx = envx; + } + int sustain_level = raw_voice.adsr [1] >> 5; + + if ( envx <= (sustain_level + 1) * 0x100 ) + voice.envstate = state_sustain; + break; + } + + case state_sustain: + // Docs: "SR [is multiplied] by the fixed value 1-1/256." + // Multiplying ENVX by 255/256 every time SUSTAIN is + // updated. + cnt -= env_rates [raw_voice.adsr [1] & 0x1f]; + if ( cnt <= 0 ) { + cnt = env_rate_init; + envx -= ((envx - 1) >> 8) + 1; + voice.envx = envx; + } + break; + } + } + else + { /* GAIN mode is set */ + /* + * Note: if the game switches between ADSR and GAIN modes + * partway through, should the count be reset, or should it + * continue from where it was? Does the DSP actually watch for + * that bit to change, or does it just go along with whatever + * it sees when it performs the update? I'm going to assume + * the latter and not update the count, unless I see a game + * that obviously wants the other behavior. The effect would + * be pretty subtle, in any case. + */ + int t = raw_voice.gain; + if (t < 0x80) + { + envx = voice.envx = t << 4; + } + else switch (t >> 5) + { + case 4: /* Docs: "Decrease (linear): Subtraction + * of the fixed value 1/64." */ + cnt -= env_rates [t & 0x1F]; + if (cnt > 0) + break; + cnt = env_rate_init; + envx -= env_range / 64; + if ( envx < 0 ) { + envx = 0; + if ( voice.envstate == state_attack ) + voice.envstate = state_decay; + } + voice.envx = envx; + break; + case 5: /* Docs: "Drecrease <sic> (exponential): + * Multiplication by the fixed value + * 1-1/256." */ + cnt -= env_rates [t & 0x1F]; + if (cnt > 0) + break; + cnt = env_rate_init; + envx -= ((envx - 1) >> 8) + 1; + if ( envx < 0 ) { + envx = 0; + if ( voice.envstate == state_attack ) + voice.envstate = state_decay; + } + voice.envx = envx; + break; + case 6: /* Docs: "Increase (linear): Addition of + * the fixed value 1/64." */ + cnt -= env_rates [t & 0x1F]; + if (cnt > 0) + break; + cnt = env_rate_init; + envx += env_range / 64; + if ( envx >= env_range ) + envx = env_range - 1; + voice.envx = envx; + break; + case 7: /* Docs: "Increase (bent line): Addition + * of the constant 1/64 up to .75 of the + * constaint <sic> 1/256 from .75 to 1." */ + cnt -= env_rates [t & 0x1F]; + if (cnt > 0) + break; + cnt = env_rate_init; + if ( envx < env_range * 3 / 4 ) + envx += env_range / 64; + else + envx += env_range / 256; + if ( envx >= env_range ) + envx = env_range - 1; + voice.envx = envx; + break; + } + } + voice.envcnt = cnt; + raw_voice.envx = envx >> 4; + return envx; +} + +// Clamp n into range -32768 <= n <= 32767 +inline int clamp_16( int n ) +{ + if ( (BOOST::int16_t) n != n ) + n = BOOST::int16_t (0x7FFF - (n >> 31)); + return n; +} + +void Spc_Dsp::run( long count, short* out_buf ) +{ + // to do: make clock_envelope() inline to avoid out-of-line calls? + + // Should we just fill the buffer with silence? Flags won't be cleared + // during this run so it seems it should keep resetting every sample. + if ( g.flags & 0x80 ) + reset(); + + struct src_dir { + char start [2]; + char loop [2]; + }; + + const src_dir* const sd = (src_dir*) &ram [g.wave_page * 0x100]; + + int const left_volume = g.left_volume * emu_gain; + int right_volume = g.right_volume * emu_gain; + if ( left_volume * right_volume < 0 ) + right_volume = -right_volume; // kill global surround + + while ( --count >= 0 ) + { + // Here we check for keys on/off. Docs say that successive writes + // to KON/KOF must be separated by at least 2 Ts periods or risk + // being neglected. Therefore DSP only looks at these during an + // update, and not at the time of the write. Only need to do this + // once however, since the regs haven't changed over the whole + // period we need to catch up with. + + g.wave_ended &= ~g.key_ons; // Keying on a voice resets that bit in ENDX. + + if ( g.noise_enables ) { + noise_count -= env_rates [g.flags & 0x1F]; + if ( noise_count <= 0 ) { + noise_count = env_rate_init; + + if ( noise & 0x4000 ) + noise_amp += (noise & 1) ? noise : -noise; + else + noise_amp >>= 1; + + int feedback = (noise << 13) ^ (noise << 14); + noise = (feedback & 0x4000) | (noise >> 1); + } + } + + // What is the expected behavior when pitch modulation is enabled on + // voice 0? Jurassic Park 2 does this. Assume 0 for now. + long prev_outx = 0; + + int echol = 0; + int echor = 0; + int left = 0; + int right = 0; + for ( int vidx = 0; vidx < voice_count; vidx++ ) + { + const int vbit = 1 << vidx; + raw_voice_t& raw_voice = voice [vidx]; + voice_t& voice = voice_state [vidx]; + + if ( voice.on_cnt && !--voice.on_cnt ) + { + // key on + keys |= vbit; + voice.addr = GET_LE16( sd [raw_voice.waveform].start ); + voice.block_remain = 1; + voice.envx = 0; + voice.block_header = 0; + voice.fraction = 0x3fff; // decode three samples immediately + voice.interp0 = 0; // BRR decoder filter uses previous two samples + voice.interp1 = 0; + + // NOTE: Real SNES does *not* appear to initialize the + // envelope counter to anything in particular. The first + // cycle always seems to come at a random time sooner than + // expected; as yet, I have been unable to find any + // pattern. I doubt it will matter though, so we'll go + // ahead and do the full time for now. + voice.envcnt = env_rate_init; + voice.envstate = state_attack; + } + + if ( g.key_ons & vbit & ~g.key_offs ) { + // voice doesn't come on if key off is set + g.key_ons &= ~vbit; + voice.on_cnt = 8; + } + + if ( keys & g.key_offs & vbit ) { + // key off + voice.envstate = state_release; + voice.on_cnt = 0; + } + + int envx; + if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 ) { + raw_voice.envx = 0; + raw_voice.outx = 0; + prev_outx = 0; + continue; + } + + // Decode samples when fraction >= 1.0 (0x1000) + for ( int n = voice.fraction >> 12; --n >= 0; ) + { + if ( !--voice.block_remain ) + { + if ( voice.block_header & 1 ) + { + g.wave_ended |= vbit; + + if ( voice.block_header & 2 ) { + // verified (played endless looping sample and ENDX was set) + voice.addr = GET_LE16( sd [raw_voice.waveform].loop ); + } + else { + // first block was end block; don't play anything (verified) + goto sample_ended; // to do: find alternative to goto + } + } + + voice.block_header = ram [voice.addr++]; + voice.block_remain = 16; // nybbles + } + + // if next block has end flag set, *this* block ends *early* (verified) + if ( voice.block_remain == 9 && (ram [voice.addr + 5] & 3) == 1 && + (voice.block_header & 3) != 3 ) + { + sample_ended: + g.wave_ended |= vbit; + keys &= ~vbit; + raw_voice.envx = 0; + voice.envx = 0; + // add silence samples to interpolation buffer + do { + voice.interp3 = voice.interp2; + voice.interp2 = voice.interp1; + voice.interp1 = voice.interp0; + voice.interp0 = 0; + } + while ( --n >= 0 ); + break; + } + + int delta = ram [voice.addr]; + if ( voice.block_remain & 1 ) { + delta <<= 4; // use lower nybble + voice.addr++; + } + + // Use sign-extended upper nybble + delta = int8_t (delta) >> 4; + + // For invalid ranges (D,E,F): if the nybble is negative, + // the result is F000. If positive, 0000. Nothing else + // like previous range, etc seems to have any effect. If + // range is valid, do the shift normally. Note these are + // both shifted right once to do the filters properly, but + // the output will be shifted back again at the end. + int shift = voice.block_header >> 4; + delta = (delta << shift) >> 1; + if ( shift > 0x0C ) + delta = (delta >> 14) & ~0x7FF; + + // One, two and three point IIR filters + int smp1 = voice.interp0; + int smp2 = voice.interp1; + switch ( (voice.block_header >> 2) & 3 ) + { + case 0: + break; + + case 1: + delta += smp1 >> 1; + delta += (-smp1) >> 5; + break; + + case 2: + delta += smp1; + delta += (-(smp1 + (smp1 >> 1))) >> 5; + delta -= smp2 >> 1; + delta += smp2 >> 5; + break; + + case 3: + delta += smp1; + delta += (-(smp1 + (smp1 << 2) + (smp1 << 3))) >> 7; + delta -= smp2 >> 1; + delta += (smp2 + (smp2 >> 1)) >> 4; + break; + } + + voice.interp3 = voice.interp2; + voice.interp2 = smp2; + voice.interp1 = smp1; + voice.interp0 = BOOST::int16_t (clamp_16( delta ) * 2); // sign-extend + } + + // rate (with possible modulation) + int rate = GET_LE16( raw_voice.rate ) & 0x3fff; + if ( g.pitch_mods & vbit ) + rate = (rate * (prev_outx + 32768)) >> 15; + + // fraction + int fraction = voice.fraction & 0xfff; + voice.fraction = fraction + rate; + fraction >>= 4; + + // Gaussian interpolation using most recent 4 samples + const short* table = gauss [fraction]; + const short* table2 = gauss [255 - fraction]; + int s = ((table [0] * voice.interp3) >> 12) + + ((table [1] * voice.interp2) >> 12) + + ((table2 [1] * voice.interp1) >> 12) + + ((table2 [0] * voice.interp0) >> 12); + int output = noise_amp; // noise is almost never used + if ( !(g.noise_enables & vbit) ) + output = clamp_16( s * 2 ); + + int muted = vbit & voices_muted; + + // scale output and set outx values + output = ((output * envx) >> 11) & ~1; + prev_outx = output; + raw_voice.outx = output >> 8; + + // apply muting if voice is externally disabled (not a SNES feature) + if ( muted ) + output = 0; + + // output + int l = (voice_vol [vidx] [0] * output) >> 7; + int r = (voice_vol [vidx] [1] * output) >> 7; + if ( g.echo_ons & vbit ) { + echol += l; + echor += r; + } + left += l; + right += r; + } // end of channel loop + + // main volume control + left = (left * left_volume) >> (7 + emu_gain_bits); + right = (right * right_volume) >> (7 + emu_gain_bits); + + // Echo FIR filter + + // read feedback from echo buffer + int echo_ptr = this->echo_ptr; + uint8_t* echo_buf = &ram [(g.echo_page * 0x100 + echo_ptr) & 0xFFFF]; + echo_ptr += 4; + if ( echo_ptr >= (g.echo_delay & 15) * 0x800 ) + echo_ptr = 0; + int fb_left = (BOOST::int16_t) GET_LE16( echo_buf ); // sign-extend + int fb_right = (BOOST::int16_t) GET_LE16( echo_buf + 2 ); // sign-extend + this->echo_ptr = echo_ptr; + + // put samples in history ring buffer + const int fir_offset = this->fir_offset; + short (*fir_pos) [2] = &fir_buf [fir_offset]; + this->fir_offset = (fir_offset + 7) & 7; // move backwards one step + fir_pos [0] [0] = fb_left; + fir_pos [0] [1] = fb_right; + fir_pos [8] [0] = fb_left; // duplicate at +8 eliminates wrap checking below + fir_pos [8] [1] = fb_right; + + // FIR + fb_left = fb_left * fir_coeff [7] + + fir_pos [1] [0] * fir_coeff [6] + + fir_pos [2] [0] * fir_coeff [5] + + fir_pos [3] [0] * fir_coeff [4] + + fir_pos [4] [0] * fir_coeff [3] + + fir_pos [5] [0] * fir_coeff [2] + + fir_pos [6] [0] * fir_coeff [1] + + fir_pos [7] [0] * fir_coeff [0]; + + fb_right = fb_right * fir_coeff [7] + + fir_pos [1] [1] * fir_coeff [6] + + fir_pos [2] [1] * fir_coeff [5] + + fir_pos [3] [1] * fir_coeff [4] + + fir_pos [4] [1] * fir_coeff [3] + + fir_pos [5] [1] * fir_coeff [2] + + fir_pos [6] [1] * fir_coeff [1] + + fir_pos [7] [1] * fir_coeff [0]; + + // overlap calculations with tests + left += (fb_left * g.left_echo_volume) >> 14; + + // echo buffer feedback + if ( !(g.flags & 0x20) ) { + echol += (fb_left * g.echo_feedback) >> 14; + echor += (fb_right * g.echo_feedback) >> 14; + SET_LE16( echo_buf, clamp_16( echol ) ); + SET_LE16( echo_buf + 2, clamp_16( echor ) ); + } + + right += (fb_right * g.right_echo_volume) >> 14; + + if ( out_buf ) + { + // write final samples + + left = clamp_16( left ); + right = clamp_16( right ); + + int mute = g.flags & 0x40; + + out_buf [0] = left; + out_buf [1] = right; + out_buf += 2; + + // muting + if ( mute ) { + out_buf [-2] = 0; + out_buf [-1] = 0; + } + } + } +} + +// Base normal_gauss table is very close to the following: +#if 0 +double e = 2.718281828; +for ( int i = 0; i < 512; i++ ) { + double x = i / 511.0 * 2.31 - 0.05; + double y = pow( e, -x * x ) * 1305.64; + normal_gauss [i] = y - 16.54; +} +#endif + +// Interleved gauss table (to improve cache coherency). +// gauss [i] [j] = normal_gauss [(1 - j) * 256 + i] +const short Spc_Dsp::gauss [256] [2] = { + 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, +}; +