Mesen-SX/Core/SPC_DSP.cpp

1091 lines
25 KiB
C++
Raw Normal View History

#include "stdafx.h"
2019-03-12 09:15:57 -04:00
// snes_spc 0.9.0. http://www.slack.net/~ant/
#include "SPC_DSP.h"
#include "blargg_endian.h"
#include <string.h>
/* 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 */
#include "blargg_source.h"
#include "Spc.h"
#include "EmuSettings.h"
#ifdef BLARGG_ENABLE_OPTIMIZER
#include BLARGG_ENABLE_OPTIMIZER
#endif
#if INT_MAX < 0x7FFFFFFF
#error "Requires that int type have at least 32 bits"
#endif
// TODO: add to blargg_endian.h
#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr ))
#define GET_LE16A( addr ) GET_LE16( addr )
#define SET_LE16A( addr, data ) SET_LE16( addr, data )
//KOF ($5C) must be initialized to $00, some games (Chester Cheetah, King of Dragons) do not initialize its value
//This causes missing sound effects in both games.
2020-12-19 23:30:09 +03:00
static BOOST::uint8_t const initial_regs[SPC_DSP::register_count] =
{
2020-12-19 23:30:09 +03:00
0x45, 0x8B, 0x5A, 0x9A, 0xE4, 0x82, 0x1B, 0x78, 0x00, 0x00, 0xAA, 0x96, 0x89, 0x0E, 0xE0, 0x80,
0x2A, 0x49, 0x3D, 0xBA, 0x14, 0xA0, 0xAC, 0xC5, 0x00, 0x00, 0x51, 0xBB, 0x9C, 0x4E, 0x7B, 0xFF,
0xF4, 0xFD, 0x57, 0x32, 0x37, 0xD9, 0x42, 0x22, 0x00, 0x00, 0x5B, 0x3C, 0x9F, 0x1B, 0x87, 0x9A,
0x6F, 0x27, 0xAF, 0x7B, 0xE5, 0x68, 0x0A, 0xD9, 0x00, 0x00, 0x9A, 0xC5, 0x9C, 0x4E, 0x7B, 0xFF,
0xEA, 0x21, 0x78, 0x4F, 0xDD, 0xED, 0x24, 0x14, 0x00, 0x00, 0x77, 0xB1, 0xD1, 0x36, 0xC1, 0x67,
0x52, 0x57, 0x46, 0x3D, 0x59, 0xF4, 0x87, 0xA4, 0x00, 0x00, 0x7E, 0x44, 0x00, 0x4E, 0x7B, 0xFF,
0x75, 0xF5, 0x06, 0x97, 0x10, 0xC3, 0x24, 0xBB, 0x00, 0x00, 0x7B, 0x7A, 0xE0, 0x60, 0x12, 0x0F,
0xF7, 0x74, 0x1C, 0xE5, 0x39, 0x3D, 0x73, 0xC1, 0x00, 0x00, 0x7A, 0xB3, 0xFF, 0x4E, 0x7B, 0xFF
};
// if ( io < -32768 ) io = -32768;
// if ( io > 32767 ) io = 32767;
#define CLAMP16( io )\
{\
if ( (int16_t) io != io )\
io = (io >> 31) ^ 0x7FFF;\
}
// Access global DSP register
#define REG(n) m.regs [r_##n]
// Access voice DSP register
#define VREG(r,n) r [v_##n]
#define WRITE_SAMPLES( l, r, out ) \
{\
out [0] = l;\
out [1] = r;\
out += 2;\
if ( out >= m.out_end )\
{\
check( out == m.out_end );\
check( m.out_end != &m.extra [extra_size] || \
(m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\
out = m.extra;\
m.out_end = &m.extra [extra_size];\
}\
2020-12-19 23:30:09 +03:00
}
2020-12-19 23:30:09 +03:00
void SPC_DSP::set_output(sample_t* out, int size)
{
2020-12-19 23:30:09 +03:00
require((size & 1) == 0); // must be even
if (!out)
{
2020-12-19 23:30:09 +03:00
out = m.extra;
size = extra_size;
}
m.out_begin = out;
2020-12-19 23:30:09 +03:00
m.out = out;
m.out_end = out + size;
}
// Volume registers and efb are signed! Easy to forget int8_t cast.
// Prefixes are to avoid accidental use of locals with same names.
2019-03-12 09:15:57 -04:00
// Gaussian interpolation
2020-12-19 23:30:09 +03:00
static short const gauss[512] =
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5,
6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17,
18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27,
28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77,
78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102,
104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132,
134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168,
171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210,
212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257,
260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311,
314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370,
374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434,
439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504,
508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577,
582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654,
659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732,
737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811,
816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889,
894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965,
969, 974, 978, 983, 988, 992, 997, 1001, 1005, 1010, 1014, 1019, 1023, 1027, 1032, 1036,
1040, 1045, 1049, 1053, 1057, 1061, 1066, 1070, 1074, 1078, 1082, 1086, 1090, 1094, 1098, 1102,
1106, 1109, 1113, 1117, 1121, 1125, 1128, 1132, 1136, 1139, 1143, 1146, 1150, 1153, 1157, 1160,
1164, 1167, 1170, 1174, 1177, 1180, 1183, 1186, 1190, 1193, 1196, 1199, 1202, 1205, 1207, 1210,
1213, 1216, 1219, 1221, 1224, 1227, 1229, 1232, 1234, 1237, 1239, 1241, 1244, 1246, 1248, 1251,
1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1270, 1272, 1274, 1275, 1277, 1279, 1280,
1282, 1283, 1284, 1286, 1287, 1288, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1297, 1298,
1299, 1300, 1300, 1301, 1302, 1302, 1303, 1303, 1303, 1304, 1304, 1304, 1304, 1304, 1305, 1305,
};
inline int SPC_DSP::interpolate_cubic(voice_t const* v)
{
int const* in = &v->buf[(v->interp_pos >> 12) + v->buf_pos];
float v0 = in[0] / 32768.0f;
float v1 = in[1] / 32768.0f;
float v2 = in[2] / 32768.0f;
float v3 = in[3] / 32768.0f;
float a = (v3 - v2) - (v0 - v1);
float b = (v0 - v1) - a;
float c = v2 - v0;
float d = v1;
2020-12-19 23:30:09 +03:00
float ratio = (float)(v->interp_pos & 0xFFF) / 0x1000;
return (int)((d + ratio * (c + ratio * (b + ratio * a))) * 32768);
}
2020-12-19 23:30:09 +03:00
inline int SPC_DSP::interpolate(voice_t const* v)
2019-03-12 09:15:57 -04:00
{
// Make pointers into gaussian based on fractional position between samples
int offset = v->interp_pos >> 4 & 0xFF;
short const* fwd = gauss + 255 - offset;
2020-12-19 23:30:09 +03:00
short const* rev = gauss + offset; // mirror left half of gaussian
int const* in = &v->buf[(v->interp_pos >> 12) + v->buf_pos];
2019-03-12 09:15:57 -04:00
int out;
2020-12-19 23:30:09 +03:00
out = (fwd[0] * in[0]) >> 11;
out += (fwd[256] * in[1]) >> 11;
out += (rev[256] * in[2]) >> 11;
out = (int16_t)out;
out += (rev[0] * in[3]) >> 11;
CLAMP16(out);
2019-03-12 09:15:57 -04:00
out &= ~1;
return out;
}
//// Counters
2019-03-12 09:15:57 -04:00
int const simple_counter_range = 2048 * 5 * 3; // 30720
2020-12-19 23:30:09 +03:00
static unsigned const counter_rates[32] =
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
simple_counter_range + 1, // never fires
2048, 1536,
1280, 1024, 768,
640, 512, 384,
320, 256, 192,
160, 128, 96,
80, 64, 48,
40, 32, 24,
20, 16, 12,
10, 8, 6,
5, 4, 3,
2,
1
2019-03-12 09:15:57 -04:00
};
2020-12-19 23:30:09 +03:00
static unsigned const counter_offsets[32] =
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
1, 0, 1040,
2019-03-12 09:15:57 -04:00
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
2020-12-19 23:30:09 +03:00
0,
0
};
inline void SPC_DSP::init_counter()
{
2019-03-12 09:15:57 -04:00
m.counter = 0;
}
2019-03-12 09:15:57 -04:00
inline void SPC_DSP::run_counters()
{
2020-12-19 23:30:09 +03:00
if (--m.counter < 0)
2019-03-12 09:15:57 -04:00
m.counter = simple_counter_range - 1;
}
2020-12-19 23:30:09 +03:00
inline unsigned SPC_DSP::read_counter(int rate)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
return ((unsigned)m.counter + counter_offsets[rate]) % counter_rates[rate];
2019-03-12 09:15:57 -04:00
}
2019-03-12 09:15:57 -04:00
//// Envelope
2020-12-19 23:30:09 +03:00
inline void SPC_DSP::run_envelope(voice_t* const v)
{
2019-03-12 09:15:57 -04:00
int env = v->env;
2020-12-19 23:30:09 +03:00
if (v->env_mode == env_release) // 60%
{
2020-12-19 23:30:09 +03:00
if ((env -= 0x8) < 0)
2019-03-12 09:15:57 -04:00
env = 0;
v->env = env;
}
else
{
int rate;
2020-12-19 23:30:09 +03:00
int env_data = VREG(v->regs, adsr1);
if (m.t_adsr0 & 0x80) // 99% ADSR
{
2020-12-19 23:30:09 +03:00
if (v->env_mode >= env_decay) // 99%
{
2019-03-12 09:15:57 -04:00
env--;
env -= env >> 8;
rate = env_data & 0x1F;
2020-12-19 23:30:09 +03:00
if (v->env_mode == env_decay) // 1%
2019-03-12 09:15:57 -04:00
rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10;
}
2019-03-12 09:15:57 -04:00
else // env_attack
{
2019-03-12 09:15:57 -04:00
rate = (m.t_adsr0 & 0x0F) * 2 + 1;
env += rate < 31 ? 0x20 : 0x400;
}
2019-03-12 09:15:57 -04:00
}
else // GAIN
{
int mode;
2020-12-19 23:30:09 +03:00
env_data = VREG(v->regs, gain);
2019-03-12 09:15:57 -04:00
mode = env_data >> 5;
2020-12-19 23:30:09 +03:00
if (mode < 4) // direct
{
2019-03-12 09:15:57 -04:00
env = env_data * 0x10;
rate = 31;
}
2019-03-12 09:15:57 -04:00
else
{
2019-03-12 09:15:57 -04:00
rate = env_data & 0x1F;
2020-12-19 23:30:09 +03:00
if (mode == 4) // 4: linear decrease
{
2019-03-12 09:15:57 -04:00
env -= 0x20;
}
2020-12-19 23:30:09 +03:00
else if (mode < 6) // 5: exponential decrease
{
2019-03-12 09:15:57 -04:00
env--;
env -= env >> 8;
}
2019-03-12 09:15:57 -04:00
else // 6,7: linear increase
{
2019-03-12 09:15:57 -04:00
env += 0x20;
2020-12-19 23:30:09 +03:00
if (mode > 6 && (unsigned)v->hidden_env >= 0x600)
2019-03-12 09:15:57 -04:00
env += 0x8 - 0x20; // 7: two-slope linear increase
}
}
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Sustain level
2020-12-19 23:30:09 +03:00
if ((env >> 8) == (env_data >> 5) && v->env_mode == env_decay)
2019-03-12 09:15:57 -04:00
v->env_mode = env_sustain;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
v->hidden_env = env;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// unsigned cast because linear decrease going negative also triggers this
2020-12-19 23:30:09 +03:00
if ((unsigned)env > 0x7FF)
2019-03-12 09:15:57 -04:00
{
env = (env < 0 ? 0 : 0x7FF);
2020-12-19 23:30:09 +03:00
if (v->env_mode == env_attack)
2019-03-12 09:15:57 -04:00
v->env_mode = env_decay;
}
2020-12-19 23:30:09 +03:00
if (!read_counter(rate))
2019-03-12 09:15:57 -04:00
v->env = env; // nothing else is controlled by the counter
}
}
//// BRR Decoding
2020-12-19 23:30:09 +03:00
inline void SPC_DSP::decode_brr(voice_t* v)
2019-03-12 09:15:57 -04:00
{
// Arrange the four input nybbles in 0xABCD order for easy decoding
int nybbles = m.t_brr_byte * 0x100 + readRam(v->brr_addr + v->brr_offset + 1);
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
int const header = m.t_brr_header;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Write to next four samples in circular buffer
2020-12-19 23:30:09 +03:00
int* pos = &v->buf[v->buf_pos];
2019-03-12 09:15:57 -04:00
int* end;
2020-12-19 23:30:09 +03:00
if ((v->buf_pos += 4) >= brr_buf_size)
2019-03-12 09:15:57 -04:00
v->buf_pos = 0;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Decode four samples
2020-12-19 23:30:09 +03:00
for (end = pos + 4; pos < end; pos++, nybbles <<= 4)
2019-03-12 09:15:57 -04:00
{
// Extract nybble and sign-extend
2020-12-19 23:30:09 +03:00
int s = (int16_t)nybbles >> 12;
2019-03-12 09:15:57 -04:00
// Shift sample based on header
int const shift = header >> 4;
s = (s << shift) >> 1;
2020-12-19 23:30:09 +03:00
if (shift >= 0xD) // handle invalid range
2019-03-12 09:15:57 -04:00
s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0)
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Apply IIR filter (8 is the most commonly used)
int const filter = header & 0x0C;
2020-12-19 23:30:09 +03:00
int const p1 = pos[brr_buf_size - 1];
int const p2 = pos[brr_buf_size - 2] >> 1;
if (filter >= 8)
2019-03-12 09:15:57 -04:00
{
s += p1;
s -= p2;
2020-12-19 23:30:09 +03:00
if (filter == 8) // s += p1 * 0.953125 - p2 * 0.46875
2019-03-12 09:15:57 -04:00
{
s += p2 >> 4;
s += (p1 * -3) >> 6;
}
else // s += p1 * 0.8984375 - p2 * 0.40625
{
s += (p1 * -13) >> 7;
s += (p2 * 3) >> 4;
}
}
2020-12-19 23:30:09 +03:00
else if (filter) // s += p1 * 0.46875
2019-03-12 09:15:57 -04:00
{
s += p1 >> 1;
s += (-p1) >> 5;
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Adjust and write sample
2020-12-19 23:30:09 +03:00
CLAMP16(s);
s = (int16_t)(s * 2);
pos[brr_buf_size] = pos[0] = s; // second copy simplifies wrap-around
2019-03-12 09:15:57 -04:00
}
}
//// Misc
#define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n()
2020-12-19 23:30:09 +03:00
MISC_CLOCK(27)
2019-03-12 09:15:57 -04:00
{
m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON
}
2020-12-19 23:30:09 +03:00
MISC_CLOCK(28)
2019-03-12 09:15:57 -04:00
{
m.t_non = REG(non);
m.t_eon = REG(eon);
m.t_dir = REG(dir);
}
2020-12-19 23:30:09 +03:00
MISC_CLOCK(29)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
if ((m.every_other_sample ^= 1) != 0)
2019-03-12 09:15:57 -04:00
m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read
}
2020-12-19 23:30:09 +03:00
MISC_CLOCK(30)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
if (m.every_other_sample)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
m.kon = m.new_kon;
m.t_koff = REG(koff) | m.mute_mask;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
run_counters();
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Noise
2020-12-19 23:30:09 +03:00
if (!read_counter(REG(flg) & 0x1F))
2019-03-12 09:15:57 -04:00
{
int feedback = (m.noise << 13) ^ (m.noise << 14);
m.noise = (feedback & 0x4000) ^ (m.noise >> 1);
}
}
//// Voices
#define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v )
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V1)
2019-03-12 09:15:57 -04:00
{
m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4;
2020-12-19 23:30:09 +03:00
m.t_srcn = VREG(v->regs, srcn);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V2)
2019-03-12 09:15:57 -04:00
{
// Read sample pointer (ignored if not needed)
uint16_t entry = m.t_dir_addr;
2020-12-19 23:30:09 +03:00
if (!v->kon_delay)
2019-03-12 09:15:57 -04:00
entry += 2;
2020-12-19 23:30:09 +03:00
m.t_brr_next_addr = readRam(entry) | (readRam(entry + 1) << 8);
m.t_adsr0 = VREG(v->regs, adsr0);
2019-03-12 09:15:57 -04:00
// Read pitch, spread over two clocks
2020-12-19 23:30:09 +03:00
m.t_pitch = VREG(v->regs, pitchl);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V3a)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
m.t_pitch += (VREG(v->regs, pitchh) & 0x3F) << 8;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V3b)
2019-03-12 09:15:57 -04:00
{
// Read BRR header and byte
2020-12-19 23:30:09 +03:00
m.t_brr_byte = readRam(v->brr_addr + v->brr_offset);
m.t_brr_header = readRam(v->brr_addr); // brr_addr doesn't need masking
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
VOICE_CLOCK(V3c)
2019-03-12 09:15:57 -04:00
{
// Pitch modulation using previous voice's output
2020-12-19 23:30:09 +03:00
if (m.t_pmon & v->vbit)
2019-03-12 09:15:57 -04:00
m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10;
2020-12-19 23:30:09 +03:00
if (v->kon_delay)
2019-03-12 09:15:57 -04:00
{
// Get ready to start BRR decoding on next sample
2020-12-19 23:30:09 +03:00
if (v->kon_delay == 5)
{
2020-12-19 23:30:09 +03:00
v->brr_addr = m.t_brr_next_addr;
v->brr_offset = 1;
v->buf_pos = 0;
2019-03-12 09:15:57 -04:00
m.t_brr_header = 0; // header is ignored on this sample
2020-12-19 23:30:09 +03:00
m.kon_check = true;
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Envelope is never run during KON
2020-12-19 23:30:09 +03:00
v->env = 0;
2019-03-12 09:15:57 -04:00
v->hidden_env = 0;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Disable BRR decoding until last three samples
v->interp_pos = 0;
2020-12-19 23:30:09 +03:00
if (--v->kon_delay & 3)
2019-03-12 09:15:57 -04:00
v->interp_pos = 0x4000;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Pitch is never added during KON
m.t_pitch = 0;
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Gaussian interpolation
{
2020-12-19 23:30:09 +03:00
int output = _settings->GetAudioConfig().EnableCubicInterpolation ? interpolate_cubic(v) : interpolate(v);
2019-03-12 09:15:57 -04:00
// Noise
2020-12-19 23:30:09 +03:00
if (m.t_non & v->vbit)
output = (int16_t)(m.noise * 2);
2019-03-12 09:15:57 -04:00
// Apply envelope
m.t_output = (output * v->env) >> 11 & ~1;
2020-12-19 23:30:09 +03:00
v->t_envx_out = (uint8_t)(v->env >> 4);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Immediate silence due to end of sample or soft reset
2020-12-19 23:30:09 +03:00
if (REG(flg) & 0x80 || (m.t_brr_header & 3) == 1)
2019-03-12 09:15:57 -04:00
{
v->env_mode = env_release;
2020-12-19 23:30:09 +03:00
v->env = 0;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
if (m.every_other_sample)
2019-03-12 09:15:57 -04:00
{
// KOFF
2020-12-19 23:30:09 +03:00
if (m.t_koff & v->vbit)
2019-03-12 09:15:57 -04:00
v->env_mode = env_release;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// KON
2020-12-19 23:30:09 +03:00
if (m.kon & v->vbit)
{
2019-03-12 09:15:57 -04:00
v->kon_delay = 5;
2020-12-19 23:30:09 +03:00
v->env_mode = env_attack;
}
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Run envelope for next sample
2020-12-19 23:30:09 +03:00
if (!v->kon_delay)
run_envelope(v);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline void SPC_DSP::voice_output(voice_t const* v, int ch)
2019-03-12 09:15:57 -04:00
{
// Apply left/right volume
2020-12-19 23:30:09 +03:00
int amp = (m.t_output * (int8_t)VREG(v->regs, voll + ch)) >> 7;
2019-03-12 09:15:57 -04:00
// Add to output total
2020-12-19 23:30:09 +03:00
m.t_main_out[ch] += amp;
CLAMP16(m.t_main_out [ch]);
2019-03-12 09:15:57 -04:00
// Optionally add to echo total
2020-12-19 23:30:09 +03:00
if (m.t_eon & v->vbit)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
m.t_echo_out[ch] += amp;
CLAMP16(m.t_echo_out [ch]);
2019-03-12 09:15:57 -04:00
}
}
2020-12-19 23:30:09 +03:00
VOICE_CLOCK(V4)
2019-03-12 09:15:57 -04:00
{
// Decode BRR
m.t_looped = 0;
2020-12-19 23:30:09 +03:00
if (v->interp_pos >= 0x4000)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
decode_brr(v);
if ((v->brr_offset += 2) >= brr_block_size)
2019-03-12 09:15:57 -04:00
{
// Start decoding next BRR block
2020-12-19 23:30:09 +03:00
assert(v->brr_offset == brr_block_size);
2019-03-12 09:15:57 -04:00
v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;
2020-12-19 23:30:09 +03:00
if (m.t_brr_header & 1)
2019-03-12 09:15:57 -04:00
{
v->brr_addr = m.t_brr_next_addr;
m.t_looped = v->vbit;
}
v->brr_offset = 1;
}
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Apply pitch
v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Keep from getting too far ahead (when using pitch modulation)
2020-12-19 23:30:09 +03:00
if (v->interp_pos > 0x7FFF)
2019-03-12 09:15:57 -04:00
v->interp_pos = 0x7FFF;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Output left
2020-12-19 23:30:09 +03:00
voice_output(v, 0);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V5)
2019-03-12 09:15:57 -04:00
{
// Output right
2020-12-19 23:30:09 +03:00
voice_output(v, 1);
2019-03-12 09:15:57 -04:00
// ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier
int endx_buf = REG(endx) | m.t_looped;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Clear bit in ENDX if KON just began
2020-12-19 23:30:09 +03:00
if (v->kon_delay == 5)
2019-03-12 09:15:57 -04:00
endx_buf &= ~v->vbit;
2020-12-19 23:30:09 +03:00
m.endx_buf = (uint8_t)endx_buf;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V6)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
(void)v; // avoid compiler warning about unused v
m.outx_buf = (uint8_t)(m.t_output >> 8);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V7)
2019-03-12 09:15:57 -04:00
{
// Update ENDX
REG(endx) = m.endx_buf;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
m.envx_buf = v->t_envx_out;
}
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V8)
2019-03-12 09:15:57 -04:00
{
// Update OUTX
2020-12-19 23:30:09 +03:00
VREG(v->regs, outx) = m.outx_buf;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V9)
2019-03-12 09:15:57 -04:00
{
// Update ENVX
2020-12-19 23:30:09 +03:00
VREG(v->regs, envx) = m.envx_buf;
2019-03-12 09:15:57 -04:00
}
// Most voices do all these in one clock, so make a handy composite
2020-12-19 23:30:09 +03:00
inline VOICE_CLOCK(V3)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
voice_V3a(v);
voice_V3b(v);
voice_V3c(v);
2019-03-12 09:15:57 -04:00
}
// Common combinations of voice steps on different voices. This greatly reduces
// code size and allows everything to be inlined in these functions.
2020-12-19 23:30:09 +03:00
VOICE_CLOCK(V7_V4_V1)
{
voice_V7(v);
voice_V1(v + 3);
voice_V4(v + 1);
}
VOICE_CLOCK(V8_V5_V2)
{
voice_V8(v);
voice_V5(v + 1);
voice_V2(v + 2);
}
VOICE_CLOCK(V9_V6_V3)
{
voice_V9(v);
voice_V6(v + 1);
voice_V3(v + 2);
}
2019-03-12 09:15:57 -04:00
//// Echo
// Current echo buffer pointer for left/right channel
#define ECHO_PTR( ch ) (m.t_echo_ptr + ch * 2)
2019-03-12 09:15:57 -04:00
// Sample in echo history buffer, where 0 is the oldest
#define ECHO_FIR( i ) (m.echo_hist_pos [i])
// Calculate FIR point for left/right channel
#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6)
#define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n()
2020-12-19 23:30:09 +03:00
inline void SPC_DSP::echo_read(int ch)
2019-03-12 09:15:57 -04:00
{
uint16_t echoPtr = ECHO_PTR(ch);
2020-12-19 23:30:09 +03:00
int16_t s = readRam(echoPtr) | (readRam(echoPtr + 1) << 8);
2019-03-12 09:15:57 -04:00
// second copy simplifies wrap-around handling
2020-12-19 23:30:09 +03:00
ECHO_FIR(0)[ch] = ECHO_FIR(8)[ch] = s >> 1;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(22)
2019-03-12 09:15:57 -04:00
{
// History
2020-12-19 23:30:09 +03:00
if (++m.echo_hist_pos >= &m.echo_hist[echo_hist_size])
2019-03-12 09:15:57 -04:00
m.echo_hist_pos = m.echo_hist;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF;
2020-12-19 23:30:09 +03:00
echo_read(0);
2019-03-12 09:15:57 -04:00
// FIR (using l and r temporaries below helps compiler optimize)
2020-12-19 23:30:09 +03:00
int l = CALC_FIR(0, 0);
int r = CALC_FIR(0, 1);
m.t_echo_in[0] = l;
m.t_echo_in[1] = r;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(23)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
int l = CALC_FIR(1, 0) + CALC_FIR(2, 0);
int r = CALC_FIR(1, 1) + CALC_FIR(2, 1);
m.t_echo_in[0] += l;
m.t_echo_in[1] += r;
echo_read(1);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(24)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
int l = CALC_FIR(3, 0) + CALC_FIR(4, 0) + CALC_FIR(5, 0);
int r = CALC_FIR(3, 1) + CALC_FIR(4, 1) + CALC_FIR(5, 1);
m.t_echo_in[0] += l;
m.t_echo_in[1] += r;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(25)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
int l = m.t_echo_in[0] + CALC_FIR(6, 0);
int r = m.t_echo_in[1] + CALC_FIR(6, 1);
l = (int16_t)l;
r = (int16_t)r;
l += (int16_t)CALC_FIR(7, 0);
r += (int16_t)CALC_FIR(7, 1);
CLAMP16(l);
CLAMP16(r);
m.t_echo_in[0] = l & ~1;
m.t_echo_in[1] = r & ~1;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
inline int SPC_DSP::echo_output(int ch)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
int out = (int16_t)((m.t_main_out[ch] * (int8_t)REG(mvoll + ch * 0x10)) >> 7) +
(int16_t)((m.t_echo_in[ch] * (int8_t)REG(evoll + ch * 0x10)) >> 7);
CLAMP16(out);
2019-03-12 09:15:57 -04:00
return out;
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(26)
2019-03-12 09:15:57 -04:00
{
// Left output volumes
// (save sample for next clock so we can output both together)
2020-12-19 23:30:09 +03:00
m.t_main_out[0] = echo_output(0);
2019-03-12 09:15:57 -04:00
// Echo feedback
2020-12-19 23:30:09 +03:00
int l = m.t_echo_out[0] + (int16_t)((m.t_echo_in[0] * (int8_t)REG(efb)) >> 7);
int r = m.t_echo_out[1] + (int16_t)((m.t_echo_in[1] * (int8_t)REG(efb)) >> 7);
CLAMP16(l);
CLAMP16(r);
m.t_echo_out[0] = l & ~1;
m.t_echo_out[1] = r & ~1;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(27)
2019-03-12 09:15:57 -04:00
{
// Output
2020-12-19 23:30:09 +03:00
int l = m.t_main_out[0];
int r = echo_output(1);
m.t_main_out[0] = 0;
m.t_main_out[1] = 0;
2019-03-12 09:15:57 -04:00
// TODO: global muting isn't this simple (turns DAC on and off
// or something, causing small ~37-sample pulse when first muted)
2020-12-19 23:30:09 +03:00
if (REG(flg) & 0x40)
2019-03-12 09:15:57 -04:00
{
l = 0;
r = 0;
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Output sample to DAC
2020-12-19 23:30:09 +03:00
#ifdef SPC_DSP_OUT_HOOK
2019-03-12 09:15:57 -04:00
SPC_DSP_OUT_HOOK( l, r );
2020-12-19 23:30:09 +03:00
#else
sample_t* out = m.out;
WRITE_SAMPLES(l, r, out);
m.out = out;
#endif
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(28)
2019-03-12 09:15:57 -04:00
{
m.t_echo_enabled = REG(flg);
}
2020-12-19 23:30:09 +03:00
inline void SPC_DSP::echo_write(int ch)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
if (!(m.t_echo_enabled & 0x20))
{
uint16_t echoPtr = ECHO_PTR(ch);
writeRam(echoPtr, m.t_echo_out[ch]);
2020-12-19 23:30:09 +03:00
writeRam(echoPtr + 1, m.t_echo_out[ch] >> 8);
2019-03-22 21:29:51 -04:00
}
2020-12-19 23:30:09 +03:00
m.t_echo_out[ch] = 0;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(29)
2019-03-12 09:15:57 -04:00
{
m.t_esa = REG(esa);
2020-12-19 23:30:09 +03:00
if (!m.echo_offset)
2019-03-12 09:15:57 -04:00
m.echo_length = (REG(edl) & 0x0F) * 0x800;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
m.echo_offset += 4;
2020-12-19 23:30:09 +03:00
if (m.echo_offset >= m.echo_length)
2019-03-12 09:15:57 -04:00
m.echo_offset = 0;
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Write left echo
2020-12-19 23:30:09 +03:00
echo_write(0);
2019-03-12 09:15:57 -04:00
m.t_echo_enabled = REG(flg);
}
2020-12-19 23:30:09 +03:00
ECHO_CLOCK(30)
2019-03-12 09:15:57 -04:00
{
// Write right echo
2020-12-19 23:30:09 +03:00
echo_write(1);
}
2019-03-12 09:15:57 -04:00
//// Timing
// Execute clock for a particular voice
#define V( clock, voice ) voice_##clock( &m.voices [voice] );
/* The most common sequence of clocks uses composite operations
for efficiency. For example, the following are equivalent to the
individual steps on the right:
V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5)
V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4)
V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */
// Voice 0 1 2 3 4 5 6 7
#define GEN_DSP_TIMING \
PHASE( 0) V(V5,0)V(V2,1)\
PHASE( 1) V(V6,0)V(V3,1)\
PHASE( 2) V(V7_V4_V1,0)\
PHASE( 3) V(V8_V5_V2,0)\
PHASE( 4) V(V9_V6_V3,0)\
PHASE( 5) V(V7_V4_V1,1)\
PHASE( 6) V(V8_V5_V2,1)\
PHASE( 7) V(V9_V6_V3,1)\
PHASE( 8) V(V7_V4_V1,2)\
PHASE( 9) V(V8_V5_V2,2)\
PHASE(10) V(V9_V6_V3,2)\
PHASE(11) V(V7_V4_V1,3)\
PHASE(12) V(V8_V5_V2,3)\
PHASE(13) V(V9_V6_V3,3)\
PHASE(14) V(V7_V4_V1,4)\
PHASE(15) V(V8_V5_V2,4)\
PHASE(16) V(V9_V6_V3,4)\
PHASE(17) V(V1,0) V(V7,5)V(V4,6)\
PHASE(18) V(V8_V5_V2,5)\
PHASE(19) V(V9_V6_V3,5)\
PHASE(20) V(V1,1) V(V7,6)V(V4,7)\
PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\
PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\
PHASE(23) V(V7,7) echo_23();\
PHASE(24) V(V8,7) echo_24();\
PHASE(25) V(V3b,0) V(V9,7) echo_25();\
PHASE(26) echo_26();\
PHASE(27) misc_27(); echo_27();\
PHASE(28) misc_28(); echo_28();\
PHASE(29) misc_29(); echo_29();\
PHASE(30) misc_30();V(V3c,0) echo_30();\
2020-12-19 23:30:09 +03:00
PHASE(31) V(V4,0) V(V1,2)
2019-03-12 09:15:57 -04:00
#if !SPC_DSP_CUSTOM_RUN
2019-03-22 21:29:51 -04:00
void SPC_DSP::run()
{
int const phase = m.phase;
m.phase = (phase + 1) & 31;
switch (phase)
{
2020-12-19 23:30:09 +03:00
#define PHASE( n ) if ( n ) break; case n:
2019-03-12 09:15:57 -04:00
GEN_DSP_TIMING
2020-12-19 23:30:09 +03:00
#undef PHASE
}
}
inline uint8_t SPC_DSP::readRam(uint16_t addr) { return _spc->DspReadRam(addr); }
inline void SPC_DSP::writeRam(uint16_t addr, uint8_t value) { _spc->DspWriteRam(addr, value); }
2019-03-12 09:15:57 -04:00
#endif
//// Setup
2020-12-19 23:30:09 +03:00
void SPC_DSP::init(Spc* spc, EmuSettings* settings, void* ram_64k)
{
_spc = spc;
_settings = settings;
2020-12-19 23:30:09 +03:00
m.ram = (uint8_t*)ram_64k;
mute_voices(0);
disable_surround(false);
set_output(0, 0);
reset();
2020-12-19 23:30:09 +03:00
#ifndef NDEBUG
// be sure this sign-extends
assert((int16_t) 0x8000 == -0x8000);
// be sure right shift preserves sign
assert((-1 >> 1) == -1);
// check clamp macro
int i;
i = +0x8000;
CLAMP16(i);
assert(i == +0x7FFF);
i = -0x8001;
CLAMP16(i);
assert(i == -0x8000);
blargg_verify_byte_order();
#endif
}
void SPC_DSP::soft_reset_common()
{
2020-12-19 23:30:09 +03:00
require(m.ram); // init() must have been called already
m.noise = 0x4000;
m.echo_hist_pos = m.echo_hist;
m.every_other_sample = 1;
2020-12-19 23:30:09 +03:00
m.echo_offset = 0;
m.phase = 0;
init_counter();
}
void SPC_DSP::soft_reset()
{
REG(flg) = 0xE0;
soft_reset_common();
}
2020-12-19 23:30:09 +03:00
void SPC_DSP::load(uint8_t const regs[register_count])
{
2020-12-19 23:30:09 +03:00
memcpy(m.regs, regs, sizeof m.regs);
memset(&m.regs[register_count], 0, offsetof(state_t, ram) - register_count);
// Internal state
2020-12-19 23:30:09 +03:00
for (int i = voice_count; --i >= 0;)
{
2020-12-19 23:30:09 +03:00
voice_t* v = &m.voices[i];
2019-03-12 09:15:57 -04:00
v->brr_offset = 1;
2020-12-19 23:30:09 +03:00
v->vbit = 1 << i;
v->regs = &m.regs[i * 0x10];
}
m.new_kon = REG(kon);
2020-12-19 23:30:09 +03:00
m.t_dir = REG(dir);
m.t_esa = REG(esa);
soft_reset_common();
}
2020-12-19 23:30:09 +03:00
void SPC_DSP::reset() { load(initial_regs); }
2019-03-12 09:15:57 -04:00
//// State save/load
#if !SPC_NO_COPY_STATE_FUNCS
2020-12-19 23:30:09 +03:00
void SPC_State_Copier::copy(void* state, size_t size)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
func(buf, state, size);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
int SPC_State_Copier::copy_int(int state, int size)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
BOOST::uint8_t s[2];
SET_LE16(s, state);
func(buf, &s, size);
return GET_LE16(s);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
void SPC_State_Copier::skip(int count)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
if (count > 0)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
char temp[64];
memset(temp, 0, sizeof temp);
2019-03-12 09:15:57 -04:00
do
{
int n = sizeof temp;
2020-12-19 23:30:09 +03:00
if (n > count)
2019-03-12 09:15:57 -04:00
n = count;
count -= n;
2020-12-19 23:30:09 +03:00
func(buf, temp, n);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
while (count);
2019-03-12 09:15:57 -04:00
}
}
void SPC_State_Copier::extra()
{
int n = 0;
SPC_State_Copier& copier = *this;
2020-12-19 23:30:09 +03:00
SPC_COPY(uint8_t, n);
skip(n);
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
void SPC_DSP::copy_state(unsigned char** io, copy_func_t copy)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
SPC_State_Copier copier(io, copy);
2019-03-12 09:15:57 -04:00
// DSP registers
2020-12-19 23:30:09 +03:00
copier.copy(m.regs, register_count);
2019-03-12 09:15:57 -04:00
// Internal state
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Voices
int i;
2020-12-19 23:30:09 +03:00
for (i = 0; i < voice_count; i++)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
voice_t* v = &m.voices[i];
2019-03-12 09:15:57 -04:00
// BRR buffer
int i;
2020-12-19 23:30:09 +03:00
for (i = 0; i < brr_buf_size; i++)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
int s = v->buf[i];
SPC_COPY(int16_t, s);
v->buf[i] = v->buf[i + brr_buf_size] = s;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
SPC_COPY(uint16_t, v->interp_pos);
SPC_COPY(uint16_t, v->brr_addr);
SPC_COPY(uint16_t, v->env);
SPC_COPY(int16_t, v->hidden_env);
SPC_COPY(uint8_t, v->buf_pos);
SPC_COPY(uint8_t, v->brr_offset);
SPC_COPY(uint8_t, v->kon_delay);
2019-03-12 09:15:57 -04:00
{
int m = v->env_mode;
2020-12-19 23:30:09 +03:00
SPC_COPY(uint8_t, m);
v->env_mode = (enum env_mode_t)m;
2019-03-12 09:15:57 -04:00
}
2020-12-19 23:30:09 +03:00
SPC_COPY(uint8_t, v->t_envx_out);
2019-03-12 09:15:57 -04:00
copier.extra();
}
2020-12-19 23:30:09 +03:00
2019-03-12 09:15:57 -04:00
// Echo history
2020-12-19 23:30:09 +03:00
for (i = 0; i < echo_hist_size; i++)
2019-03-12 09:15:57 -04:00
{
int j;
2020-12-19 23:30:09 +03:00
for (j = 0; j < 2; j++)
2019-03-12 09:15:57 -04:00
{
2020-12-19 23:30:09 +03:00
int s = m.echo_hist_pos[i][j];
SPC_COPY(int16_t, s);
m.echo_hist[i][j] = s; // write back at offset 0
2019-03-12 09:15:57 -04:00
}
}
m.echo_hist_pos = m.echo_hist;
2020-12-19 23:30:09 +03:00
memcpy(&m.echo_hist[echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist[0]);
2019-03-12 09:15:57 -04:00
// Misc
2020-12-19 23:30:09 +03:00
SPC_COPY(uint8_t, m.every_other_sample);
SPC_COPY(uint8_t, m.kon);
SPC_COPY(uint16_t, m.noise);
SPC_COPY(uint16_t, m.counter);
SPC_COPY(uint16_t, m.echo_offset);
SPC_COPY(uint16_t, m.echo_length);
SPC_COPY(uint8_t, m.phase);
SPC_COPY(uint8_t, m.new_kon);
SPC_COPY(uint8_t, m.endx_buf);
SPC_COPY(uint8_t, m.envx_buf);
SPC_COPY(uint8_t, m.outx_buf);
SPC_COPY(uint8_t, m.t_pmon);
SPC_COPY(uint8_t, m.t_non);
SPC_COPY(uint8_t, m.t_eon);
SPC_COPY(uint8_t, m.t_dir);
SPC_COPY(uint8_t, m.t_koff);
SPC_COPY(uint16_t, m.t_brr_next_addr);
SPC_COPY(uint8_t, m.t_adsr0);
SPC_COPY(uint8_t, m.t_brr_header);
SPC_COPY(uint8_t, m.t_brr_byte);
SPC_COPY(uint8_t, m.t_srcn);
SPC_COPY(uint8_t, m.t_esa);
SPC_COPY(uint8_t, m.t_echo_enabled);
SPC_COPY(int16_t, m.t_main_out [0]);
SPC_COPY(int16_t, m.t_main_out [1]);
SPC_COPY(int16_t, m.t_echo_out [0]);
SPC_COPY(int16_t, m.t_echo_out [1]);
SPC_COPY(int16_t, m.t_echo_in [0]);
SPC_COPY(int16_t, m.t_echo_in [1]);
SPC_COPY(uint16_t, m.t_dir_addr);
SPC_COPY(uint16_t, m.t_pitch);
SPC_COPY(int16_t, m.t_output);
SPC_COPY(uint16_t, m.t_echo_ptr);
SPC_COPY(uint8_t, m.t_looped);
2019-03-12 09:15:57 -04:00
copier.extra();
}
#endif