annotate snes_spc/SPC_DSP.cpp @ 1:2ad543b339f1

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