Mercurial > spc_convert
view snes_spc/SPC_DSP.cpp @ 0:e38dacceb958
initial import
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 21 Oct 2011 05:53:11 -0700 |
parents | |
children |
line wrap: on
line source
1 // snes_spc 0.9.0. http://www.slack.net/~ant/3 #include "SPC_DSP.h"5 #include "blargg_endian.h"6 #include <string.h>8 /* Copyright (C) 2007 Shay Green. This module is free software; you9 can redistribute it and/or modify it under the terms of the GNU Lesser10 General Public License as published by the Free Software Foundation; either11 version 2.1 of the License, or (at your option) any later version. This12 module is distributed in the hope that it will be useful, but WITHOUT ANY13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more15 details. You should have received a copy of the GNU Lesser General Public16 License along with this module; if not, write to the Free Software Foundation,17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */19 #include "blargg_source.h"21 #ifdef BLARGG_ENABLE_OPTIMIZER22 #include BLARGG_ENABLE_OPTIMIZER23 #endif25 #if INT_MAX < 0x7FFFFFFF26 #error "Requires that int type have at least 32 bits"27 #endif29 // TODO: add to blargg_endian.h30 #define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr ))31 #define GET_LE16A( addr ) GET_LE16( addr )32 #define SET_LE16A( addr, data ) SET_LE16( addr, data )34 static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =35 {36 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80,37 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF,38 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A,39 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF,40 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67,41 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF,42 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F,43 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF44 };46 // if ( io < -32768 ) io = -32768;47 // if ( io > 32767 ) io = 32767;48 #define CLAMP16( io )\49 {\50 if ( (int16_t) io != io )\51 io = (io >> 31) ^ 0x7FFF;\52 }54 // Access global DSP register55 #define REG(n) m.regs [r_##n]57 // Access voice DSP register58 #define VREG(r,n) r [v_##n]60 #define WRITE_SAMPLES( l, r, out ) \61 {\62 out [0] = l;\63 out [1] = r;\64 out += 2;\65 if ( out >= m.out_end )\66 {\67 check( out == m.out_end );\68 check( m.out_end != &m.extra [extra_size] || \69 (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\70 out = m.extra;\71 m.out_end = &m.extra [extra_size];\72 }\73 }\75 void SPC_DSP::set_output( sample_t* out, int size )76 {77 require( (size & 1) == 0 ); // must be even78 if ( !out )79 {80 out = m.extra;81 size = extra_size;82 }83 m.out_begin = out;84 m.out = out;85 m.out_end = out + size;86 }88 // Volume registers and efb are signed! Easy to forget int8_t cast.89 // Prefixes are to avoid accidental use of locals with same names.91 // Gaussian interpolation93 static short const gauss [512] =94 {95 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,96 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,97 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5,98 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,99 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17,100 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27,101 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40,102 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,103 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77,104 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102,105 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132,106 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168,107 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210,108 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257,109 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311,110 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370,111 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434,112 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504,113 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577,114 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654,115 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732,116 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811,117 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889,118 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965,119 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036,120 1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102,121 1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160,122 1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210,123 1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251,124 1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280,125 1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298,126 1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305,127 };129 inline int SPC_DSP::interpolate( voice_t const* v )130 {131 // Make pointers into gaussian based on fractional position between samples132 int offset = v->interp_pos >> 4 & 0xFF;133 short const* fwd = gauss + 255 - offset;134 short const* rev = gauss + offset; // mirror left half of gaussian136 int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos];137 int out;138 out = (fwd [ 0] * in [0]) >> 11;139 out += (fwd [256] * in [1]) >> 11;140 out += (rev [256] * in [2]) >> 11;141 out = (int16_t) out;142 out += (rev [ 0] * in [3]) >> 11;144 CLAMP16( out );145 out &= ~1;146 return out;147 }150 //// Counters152 int const simple_counter_range = 2048 * 5 * 3; // 30720154 static unsigned const counter_rates [32] =155 {156 simple_counter_range + 1, // never fires157 2048, 1536,158 1280, 1024, 768,159 640, 512, 384,160 320, 256, 192,161 160, 128, 96,162 80, 64, 48,163 40, 32, 24,164 20, 16, 12,165 10, 8, 6,166 5, 4, 3,167 2,168 1169 };171 static unsigned const counter_offsets [32] =172 {173 1, 0, 1040,174 536, 0, 1040,175 536, 0, 1040,176 536, 0, 1040,177 536, 0, 1040,178 536, 0, 1040,179 536, 0, 1040,180 536, 0, 1040,181 536, 0, 1040,182 536, 0, 1040,183 0,184 0185 };187 inline void SPC_DSP::init_counter()188 {189 m.counter = 0;190 }192 inline void SPC_DSP::run_counters()193 {194 if ( --m.counter < 0 )195 m.counter = simple_counter_range - 1;196 }198 inline unsigned SPC_DSP::read_counter( int rate )199 {200 return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate];201 }204 //// Envelope206 inline void SPC_DSP::run_envelope( voice_t* const v )207 {208 int env = v->env;209 if ( v->env_mode == env_release ) // 60%210 {211 if ( (env -= 0x8) < 0 )212 env = 0;213 v->env = env;214 }215 else216 {217 int rate;218 int env_data = VREG(v->regs,adsr1);219 if ( m.t_adsr0 & 0x80 ) // 99% ADSR220 {221 if ( v->env_mode >= env_decay ) // 99%222 {223 env--;224 env -= env >> 8;225 rate = env_data & 0x1F;226 if ( v->env_mode == env_decay ) // 1%227 rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10;228 }229 else // env_attack230 {231 rate = (m.t_adsr0 & 0x0F) * 2 + 1;232 env += rate < 31 ? 0x20 : 0x400;233 }234 }235 else // GAIN236 {237 int mode;238 env_data = VREG(v->regs,gain);239 mode = env_data >> 5;240 if ( mode < 4 ) // direct241 {242 env = env_data * 0x10;243 rate = 31;244 }245 else246 {247 rate = env_data & 0x1F;248 if ( mode == 4 ) // 4: linear decrease249 {250 env -= 0x20;251 }252 else if ( mode < 6 ) // 5: exponential decrease253 {254 env--;255 env -= env >> 8;256 }257 else // 6,7: linear increase258 {259 env += 0x20;260 if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 )261 env += 0x8 - 0x20; // 7: two-slope linear increase262 }263 }264 }266 // Sustain level267 if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay )268 v->env_mode = env_sustain;270 v->hidden_env = env;272 // unsigned cast because linear decrease going negative also triggers this273 if ( (unsigned) env > 0x7FF )274 {275 env = (env < 0 ? 0 : 0x7FF);276 if ( v->env_mode == env_attack )277 v->env_mode = env_decay;278 }280 if ( !read_counter( rate ) )281 v->env = env; // nothing else is controlled by the counter282 }283 }286 //// BRR Decoding288 inline void SPC_DSP::decode_brr( voice_t* v )289 {290 // Arrange the four input nybbles in 0xABCD order for easy decoding291 int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];293 int const header = m.t_brr_header;295 // Write to next four samples in circular buffer296 int* pos = &v->buf [v->buf_pos];297 int* end;298 if ( (v->buf_pos += 4) >= brr_buf_size )299 v->buf_pos = 0;301 // Decode four samples302 for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )303 {304 // Extract nybble and sign-extend305 int s = (int16_t) nybbles >> 12;307 // Shift sample based on header308 int const shift = header >> 4;309 s = (s << shift) >> 1;310 if ( shift >= 0xD ) // handle invalid range311 s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0)313 // Apply IIR filter (8 is the most commonly used)314 int const filter = header & 0x0C;315 int const p1 = pos [brr_buf_size - 1];316 int const p2 = pos [brr_buf_size - 2] >> 1;317 if ( filter >= 8 )318 {319 s += p1;320 s -= p2;321 if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875322 {323 s += p2 >> 4;324 s += (p1 * -3) >> 6;325 }326 else // s += p1 * 0.8984375 - p2 * 0.40625327 {328 s += (p1 * -13) >> 7;329 s += (p2 * 3) >> 4;330 }331 }332 else if ( filter ) // s += p1 * 0.46875333 {334 s += p1 >> 1;335 s += (-p1) >> 5;336 }338 // Adjust and write sample339 CLAMP16( s );340 s = (int16_t) (s * 2);341 pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around342 }343 }346 //// Misc348 #define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n()350 MISC_CLOCK( 27 )351 {352 m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON353 }354 MISC_CLOCK( 28 )355 {356 m.t_non = REG(non);357 m.t_eon = REG(eon);358 m.t_dir = REG(dir);359 }360 MISC_CLOCK( 29 )361 {362 if ( (m.every_other_sample ^= 1) != 0 )363 m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read364 }365 MISC_CLOCK( 30 )366 {367 if ( m.every_other_sample )368 {369 m.kon = m.new_kon;370 m.t_koff = REG(koff) | m.mute_mask;371 }373 run_counters();375 // Noise376 if ( !read_counter( REG(flg) & 0x1F ) )377 {378 int feedback = (m.noise << 13) ^ (m.noise << 14);379 m.noise = (feedback & 0x4000) ^ (m.noise >> 1);380 }381 }384 //// Voices386 #define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v )388 inline VOICE_CLOCK( V1 )389 {390 m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4;391 m.t_srcn = VREG(v->regs,srcn);392 }393 inline VOICE_CLOCK( V2 )394 {395 // Read sample pointer (ignored if not needed)396 uint8_t const* entry = &m.ram [m.t_dir_addr];397 if ( !v->kon_delay )398 entry += 2;399 m.t_brr_next_addr = GET_LE16A( entry );401 m.t_adsr0 = VREG(v->regs,adsr0);403 // Read pitch, spread over two clocks404 m.t_pitch = VREG(v->regs,pitchl);405 }406 inline VOICE_CLOCK( V3a )407 {408 m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8;409 }410 inline VOICE_CLOCK( V3b )411 {412 // Read BRR header and byte413 m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF];414 m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking415 }416 VOICE_CLOCK( V3c )417 {418 // Pitch modulation using previous voice's output419 if ( m.t_pmon & v->vbit )420 m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10;422 if ( v->kon_delay )423 {424 // Get ready to start BRR decoding on next sample425 if ( v->kon_delay == 5 )426 {427 v->brr_addr = m.t_brr_next_addr;428 v->brr_offset = 1;429 v->buf_pos = 0;430 m.t_brr_header = 0; // header is ignored on this sample431 m.kon_check = true;432 }434 // Envelope is never run during KON435 v->env = 0;436 v->hidden_env = 0;438 // Disable BRR decoding until last three samples439 v->interp_pos = 0;440 if ( --v->kon_delay & 3 )441 v->interp_pos = 0x4000;443 // Pitch is never added during KON444 m.t_pitch = 0;445 }447 // Gaussian interpolation448 {449 int output = interpolate( v );451 // Noise452 if ( m.t_non & v->vbit )453 output = (int16_t) (m.noise * 2);455 // Apply envelope456 m.t_output = (output * v->env) >> 11 & ~1;457 v->t_envx_out = (uint8_t) (v->env >> 4);458 }460 // Immediate silence due to end of sample or soft reset461 if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 )462 {463 v->env_mode = env_release;464 v->env = 0;465 }467 if ( m.every_other_sample )468 {469 // KOFF470 if ( m.t_koff & v->vbit )471 v->env_mode = env_release;473 // KON474 if ( m.kon & v->vbit )475 {476 v->kon_delay = 5;477 v->env_mode = env_attack;478 }479 }481 // Run envelope for next sample482 if ( !v->kon_delay )483 run_envelope( v );484 }485 inline void SPC_DSP::voice_output( voice_t const* v, int ch )486 {487 // Apply left/right volume488 int amp = (m.t_output * (int8_t) VREG(v->regs,voll + ch)) >> 7;490 // Add to output total491 m.t_main_out [ch] += amp;492 CLAMP16( m.t_main_out [ch] );494 // Optionally add to echo total495 if ( m.t_eon & v->vbit )496 {497 m.t_echo_out [ch] += amp;498 CLAMP16( m.t_echo_out [ch] );499 }500 }501 VOICE_CLOCK( V4 )502 {503 // Decode BRR504 m.t_looped = 0;505 if ( v->interp_pos >= 0x4000 )506 {507 decode_brr( v );509 if ( (v->brr_offset += 2) >= brr_block_size )510 {511 // Start decoding next BRR block512 assert( v->brr_offset == brr_block_size );513 v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;514 if ( m.t_brr_header & 1 )515 {516 v->brr_addr = m.t_brr_next_addr;517 m.t_looped = v->vbit;518 }519 v->brr_offset = 1;520 }521 }523 // Apply pitch524 v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch;526 // Keep from getting too far ahead (when using pitch modulation)527 if ( v->interp_pos > 0x7FFF )528 v->interp_pos = 0x7FFF;530 // Output left531 voice_output( v, 0 );532 }533 inline VOICE_CLOCK( V5 )534 {535 // Output right536 voice_output( v, 1 );538 // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier539 int endx_buf = REG(endx) | m.t_looped;541 // Clear bit in ENDX if KON just began542 if ( v->kon_delay == 5 )543 endx_buf &= ~v->vbit;544 m.endx_buf = (uint8_t) endx_buf;545 }546 inline VOICE_CLOCK( V6 )547 {548 (void) v; // avoid compiler warning about unused v549 m.outx_buf = (uint8_t) (m.t_output >> 8);550 }551 inline VOICE_CLOCK( V7 )552 {553 // Update ENDX554 REG(endx) = m.endx_buf;556 m.envx_buf = v->t_envx_out;557 }558 inline VOICE_CLOCK( V8 )559 {560 // Update OUTX561 VREG(v->regs,outx) = m.outx_buf;562 }563 inline VOICE_CLOCK( V9 )564 {565 // Update ENVX566 VREG(v->regs,envx) = m.envx_buf;567 }569 // Most voices do all these in one clock, so make a handy composite570 inline VOICE_CLOCK( V3 )571 {572 voice_V3a( v );573 voice_V3b( v );574 voice_V3c( v );575 }577 // Common combinations of voice steps on different voices. This greatly reduces578 // code size and allows everything to be inlined in these functions.579 VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); }580 VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); }581 VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); }584 //// Echo586 // Current echo buffer pointer for left/right channel587 #define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2])589 // Sample in echo history buffer, where 0 is the oldest590 #define ECHO_FIR( i ) (m.echo_hist_pos [i])592 // Calculate FIR point for left/right channel593 #define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6)595 #define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n()597 inline void SPC_DSP::echo_read( int ch )598 {599 int s = GET_LE16SA( ECHO_PTR( ch ) );600 // second copy simplifies wrap-around handling601 ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1;602 }604 ECHO_CLOCK( 22 )605 {606 // History607 if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] )608 m.echo_hist_pos = m.echo_hist;610 m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF;611 echo_read( 0 );613 // FIR (using l and r temporaries below helps compiler optimize)614 int l = CALC_FIR( 0, 0 );615 int r = CALC_FIR( 0, 1 );617 m.t_echo_in [0] = l;618 m.t_echo_in [1] = r;619 }620 ECHO_CLOCK( 23 )621 {622 int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 );623 int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 );625 m.t_echo_in [0] += l;626 m.t_echo_in [1] += r;628 echo_read( 1 );629 }630 ECHO_CLOCK( 24 )631 {632 int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 );633 int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 );635 m.t_echo_in [0] += l;636 m.t_echo_in [1] += r;637 }638 ECHO_CLOCK( 25 )639 {640 int l = m.t_echo_in [0] + CALC_FIR( 6, 0 );641 int r = m.t_echo_in [1] + CALC_FIR( 6, 1 );643 l = (int16_t) l;644 r = (int16_t) r;646 l += (int16_t) CALC_FIR( 7, 0 );647 r += (int16_t) CALC_FIR( 7, 1 );649 CLAMP16( l );650 CLAMP16( r );652 m.t_echo_in [0] = l & ~1;653 m.t_echo_in [1] = r & ~1;654 }655 inline int SPC_DSP::echo_output( int ch )656 {657 int out = (int16_t) ((m.t_main_out [ch] * (int8_t) REG(mvoll + ch * 0x10)) >> 7) +658 (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7);659 CLAMP16( out );660 return out;661 }662 ECHO_CLOCK( 26 )663 {664 // Left output volumes665 // (save sample for next clock so we can output both together)666 m.t_main_out [0] = echo_output( 0 );668 // Echo feedback669 int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7);670 int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7);672 CLAMP16( l );673 CLAMP16( r );675 m.t_echo_out [0] = l & ~1;676 m.t_echo_out [1] = r & ~1;677 }678 ECHO_CLOCK( 27 )679 {680 // Output681 int l = m.t_main_out [0];682 int r = echo_output( 1 );683 m.t_main_out [0] = 0;684 m.t_main_out [1] = 0;686 // TODO: global muting isn't this simple (turns DAC on and off687 // or something, causing small ~37-sample pulse when first muted)688 if ( REG(flg) & 0x40 )689 {690 l = 0;691 r = 0;692 }694 // Output sample to DAC695 #ifdef SPC_DSP_OUT_HOOK696 SPC_DSP_OUT_HOOK( l, r );697 #else698 sample_t* out = m.out;699 WRITE_SAMPLES( l, r, out );700 m.out = out;701 #endif702 }703 ECHO_CLOCK( 28 )704 {705 m.t_echo_enabled = REG(flg);706 }707 inline void SPC_DSP::echo_write( int ch )708 {709 if ( !(m.t_echo_enabled & 0x20) )710 SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] );711 m.t_echo_out [ch] = 0;712 }713 ECHO_CLOCK( 29 )714 {715 m.t_esa = REG(esa);717 if ( !m.echo_offset )718 m.echo_length = (REG(edl) & 0x0F) * 0x800;720 m.echo_offset += 4;721 if ( m.echo_offset >= m.echo_length )722 m.echo_offset = 0;724 // Write left echo725 echo_write( 0 );727 m.t_echo_enabled = REG(flg);728 }729 ECHO_CLOCK( 30 )730 {731 // Write right echo732 echo_write( 1 );733 }736 //// Timing738 // Execute clock for a particular voice739 #define V( clock, voice ) voice_##clock( &m.voices [voice] );741 /* The most common sequence of clocks uses composite operations742 for efficiency. For example, the following are equivalent to the743 individual steps on the right:745 V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5)746 V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4)747 V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */749 // Voice 0 1 2 3 4 5 6 7750 #define GEN_DSP_TIMING \751 PHASE( 0) V(V5,0)V(V2,1)\752 PHASE( 1) V(V6,0)V(V3,1)\753 PHASE( 2) V(V7_V4_V1,0)\754 PHASE( 3) V(V8_V5_V2,0)\755 PHASE( 4) V(V9_V6_V3,0)\756 PHASE( 5) V(V7_V4_V1,1)\757 PHASE( 6) V(V8_V5_V2,1)\758 PHASE( 7) V(V9_V6_V3,1)\759 PHASE( 8) V(V7_V4_V1,2)\760 PHASE( 9) V(V8_V5_V2,2)\761 PHASE(10) V(V9_V6_V3,2)\762 PHASE(11) V(V7_V4_V1,3)\763 PHASE(12) V(V8_V5_V2,3)\764 PHASE(13) V(V9_V6_V3,3)\765 PHASE(14) V(V7_V4_V1,4)\766 PHASE(15) V(V8_V5_V2,4)\767 PHASE(16) V(V9_V6_V3,4)\768 PHASE(17) V(V1,0) V(V7,5)V(V4,6)\769 PHASE(18) V(V8_V5_V2,5)\770 PHASE(19) V(V9_V6_V3,5)\771 PHASE(20) V(V1,1) V(V7,6)V(V4,7)\772 PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\773 PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\774 PHASE(23) V(V7,7) echo_23();\775 PHASE(24) V(V8,7) echo_24();\776 PHASE(25) V(V3b,0) V(V9,7) echo_25();\777 PHASE(26) echo_26();\778 PHASE(27) misc_27(); echo_27();\779 PHASE(28) misc_28(); echo_28();\780 PHASE(29) misc_29(); echo_29();\781 PHASE(30) misc_30();V(V3c,0) echo_30();\782 PHASE(31) V(V4,0) V(V1,2)\784 #if !SPC_DSP_CUSTOM_RUN786 void SPC_DSP::run( int clocks_remain )787 {788 require( clocks_remain > 0 );790 int const phase = m.phase;791 m.phase = (phase + clocks_remain) & 31;792 switch ( phase )793 {794 loop:796 #define PHASE( n ) if ( n && !--clocks_remain ) break; case n:797 GEN_DSP_TIMING798 #undef PHASE800 if ( --clocks_remain )801 goto loop;802 }803 }805 #endif808 //// Setup810 void SPC_DSP::init( void* ram_64k )811 {812 m.ram = (uint8_t*) ram_64k;813 mute_voices( 0 );814 disable_surround( false );815 set_output( 0, 0 );816 reset();818 #ifndef NDEBUG819 // be sure this sign-extends820 assert( (int16_t) 0x8000 == -0x8000 );822 // be sure right shift preserves sign823 assert( (-1 >> 1) == -1 );825 // check clamp macro826 int i;827 i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF );828 i = -0x8001; CLAMP16( i ); assert( i == -0x8000 );830 blargg_verify_byte_order();831 #endif832 }834 void SPC_DSP::soft_reset_common()835 {836 require( m.ram ); // init() must have been called already838 m.noise = 0x4000;839 m.echo_hist_pos = m.echo_hist;840 m.every_other_sample = 1;841 m.echo_offset = 0;842 m.phase = 0;844 init_counter();845 }847 void SPC_DSP::soft_reset()848 {849 REG(flg) = 0xE0;850 soft_reset_common();851 }853 void SPC_DSP::load( uint8_t const regs [register_count] )854 {855 memcpy( m.regs, regs, sizeof m.regs );856 memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );858 // Internal state859 for ( int i = voice_count; --i >= 0; )860 {861 voice_t* v = &m.voices [i];862 v->brr_offset = 1;863 v->vbit = 1 << i;864 v->regs = &m.regs [i * 0x10];865 }866 m.new_kon = REG(kon);867 m.t_dir = REG(dir);868 m.t_esa = REG(esa);870 soft_reset_common();871 }873 void SPC_DSP::reset() { load( initial_regs ); }876 //// State save/load878 #if !SPC_NO_COPY_STATE_FUNCS880 void SPC_State_Copier::copy( void* state, size_t size )881 {882 func( buf, state, size );883 }885 int SPC_State_Copier::copy_int( int state, int size )886 {887 BOOST::uint8_t s [2];888 SET_LE16( s, state );889 func( buf, &s, size );890 return GET_LE16( s );891 }893 void SPC_State_Copier::skip( int count )894 {895 if ( count > 0 )896 {897 char temp [64];898 memset( temp, 0, sizeof temp );899 do900 {901 int n = sizeof temp;902 if ( n > count )903 n = count;904 count -= n;905 func( buf, temp, n );906 }907 while ( count );908 }909 }911 void SPC_State_Copier::extra()912 {913 int n = 0;914 SPC_State_Copier& copier = *this;915 SPC_COPY( uint8_t, n );916 skip( n );917 }919 void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy )920 {921 SPC_State_Copier copier( io, copy );923 // DSP registers924 copier.copy( m.regs, register_count );926 // Internal state928 // Voices929 int i;930 for ( i = 0; i < voice_count; i++ )931 {932 voice_t* v = &m.voices [i];934 // BRR buffer935 int i;936 for ( i = 0; i < brr_buf_size; i++ )937 {938 int s = v->buf [i];939 SPC_COPY( int16_t, s );940 v->buf [i] = v->buf [i + brr_buf_size] = s;941 }943 SPC_COPY( uint16_t, v->interp_pos );944 SPC_COPY( uint16_t, v->brr_addr );945 SPC_COPY( uint16_t, v->env );946 SPC_COPY( int16_t, v->hidden_env );947 SPC_COPY( uint8_t, v->buf_pos );948 SPC_COPY( uint8_t, v->brr_offset );949 SPC_COPY( uint8_t, v->kon_delay );950 {951 int m = v->env_mode;952 SPC_COPY( uint8_t, m );953 v->env_mode = (enum env_mode_t) m;954 }955 SPC_COPY( uint8_t, v->t_envx_out );957 copier.extra();958 }960 // Echo history961 for ( i = 0; i < echo_hist_size; i++ )962 {963 int j;964 for ( j = 0; j < 2; j++ )965 {966 int s = m.echo_hist_pos [i] [j];967 SPC_COPY( int16_t, s );968 m.echo_hist [i] [j] = s; // write back at offset 0969 }970 }971 m.echo_hist_pos = m.echo_hist;972 memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] );974 // Misc975 SPC_COPY( uint8_t, m.every_other_sample );976 SPC_COPY( uint8_t, m.kon );978 SPC_COPY( uint16_t, m.noise );979 SPC_COPY( uint16_t, m.counter );980 SPC_COPY( uint16_t, m.echo_offset );981 SPC_COPY( uint16_t, m.echo_length );982 SPC_COPY( uint8_t, m.phase );984 SPC_COPY( uint8_t, m.new_kon );985 SPC_COPY( uint8_t, m.endx_buf );986 SPC_COPY( uint8_t, m.envx_buf );987 SPC_COPY( uint8_t, m.outx_buf );989 SPC_COPY( uint8_t, m.t_pmon );990 SPC_COPY( uint8_t, m.t_non );991 SPC_COPY( uint8_t, m.t_eon );992 SPC_COPY( uint8_t, m.t_dir );993 SPC_COPY( uint8_t, m.t_koff );995 SPC_COPY( uint16_t, m.t_brr_next_addr );996 SPC_COPY( uint8_t, m.t_adsr0 );997 SPC_COPY( uint8_t, m.t_brr_header );998 SPC_COPY( uint8_t, m.t_brr_byte );999 SPC_COPY( uint8_t, m.t_srcn );1000 SPC_COPY( uint8_t, m.t_esa );1001 SPC_COPY( uint8_t, m.t_echo_enabled );1003 SPC_COPY( int16_t, m.t_main_out [0] );1004 SPC_COPY( int16_t, m.t_main_out [1] );1005 SPC_COPY( int16_t, m.t_echo_out [0] );1006 SPC_COPY( int16_t, m.t_echo_out [1] );1007 SPC_COPY( int16_t, m.t_echo_in [0] );1008 SPC_COPY( int16_t, m.t_echo_in [1] );1010 SPC_COPY( uint16_t, m.t_dir_addr );1011 SPC_COPY( uint16_t, m.t_pitch );1012 SPC_COPY( int16_t, m.t_output );1013 SPC_COPY( uint16_t, m.t_echo_ptr );1014 SPC_COPY( uint8_t, m.t_looped );1016 copier.extra();1017 }1018 #endif