2144 lines
78 KiB
C
2144 lines
78 KiB
C
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// spucore - Emulates a single SPU CORE
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef EMU_COMPILE
|
|
#error "Hi I forgot to set EMU_COMPILE"
|
|
#endif
|
|
|
|
#include "spucore.h"
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** Key-on defer
|
|
**
|
|
** Bit of a hack I did to stop the clicking in Final Fantasy Tactics
|
|
** If there's a key-on while the envelope is still active, the key-on is
|
|
** deferred by this many samples while the existing channel is microramped
|
|
** down to zero (works best if it's a power of 2)
|
|
*/
|
|
|
|
//#define KEYON_DEFER_SAMPLES (64)
|
|
#define KEYON_DEFER_SAMPLES (64)
|
|
|
|
/* This is a bit of a hack that fixes Dragon Ball - Final Bout: Title
|
|
** Screen (Track 29), by ignoring any keyoffs that occur immediately
|
|
** after a keyon. The developer who tested this determined a delay of
|
|
** 384 cycles, but we only operate in whole sample granularity, which
|
|
** is 768 cycles at a time. So we'll just hack around a delay of one
|
|
** sample. */
|
|
|
|
#define KEYON_KEYOFF_BLOCK_SAMPLES (1)
|
|
|
|
//
|
|
// Render max samples
|
|
//
|
|
#define RENDERMAX (200)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** Static information
|
|
*/
|
|
static const sint16 gauss_shuffled_reverse_table[1024] = {
|
|
0x12c7, 0x59b3, 0x1307, 0xffff, 0x1288, 0x59b2, 0x1347, 0xffff, 0x1249, 0x59b0, 0x1388, 0xffff, 0x120b, 0x59ad, 0x13c9, 0xffff,
|
|
0x11cd, 0x59a9, 0x140b, 0xffff, 0x118f, 0x59a4, 0x144d, 0xffff, 0x1153, 0x599e, 0x1490, 0xffff, 0x1116, 0x5997, 0x14d4, 0xffff,
|
|
0x10db, 0x598f, 0x1517, 0xffff, 0x109f, 0x5986, 0x155c, 0xffff, 0x1065, 0x597c, 0x15a0, 0xffff, 0x102a, 0x5971, 0x15e6, 0xffff,
|
|
0x0ff1, 0x5965, 0x162c, 0xffff, 0x0fb7, 0x5958, 0x1672, 0xffff, 0x0f7f, 0x5949, 0x16b9, 0xffff, 0x0f46, 0x593a, 0x1700, 0xffff,
|
|
0x0f0f, 0x592a, 0x1747, 0x0000, 0x0ed7, 0x5919, 0x1790, 0x0000, 0x0ea1, 0x5907, 0x17d8, 0x0000, 0x0e6b, 0x58f4, 0x1821, 0x0000,
|
|
0x0e35, 0x58e0, 0x186b, 0x0000, 0x0e00, 0x58cb, 0x18b5, 0x0000, 0x0dcb, 0x58b5, 0x1900, 0x0000, 0x0d97, 0x589e, 0x194b, 0x0001,
|
|
0x0d63, 0x5886, 0x1996, 0x0001, 0x0d30, 0x586d, 0x19e2, 0x0001, 0x0cfd, 0x5853, 0x1a2e, 0x0001, 0x0ccb, 0x5838, 0x1a7b, 0x0002,
|
|
0x0c99, 0x581c, 0x1ac8, 0x0002, 0x0c68, 0x57ff, 0x1b16, 0x0002, 0x0c38, 0x57e2, 0x1b64, 0x0003, 0x0c07, 0x57c3, 0x1bb3, 0x0003,
|
|
0x0bd8, 0x57a3, 0x1c02, 0x0003, 0x0ba9, 0x5782, 0x1c51, 0x0004, 0x0b7a, 0x5761, 0x1ca1, 0x0004, 0x0b4c, 0x573e, 0x1cf1, 0x0005,
|
|
0x0b1e, 0x571b, 0x1d42, 0x0005, 0x0af1, 0x56f6, 0x1d93, 0x0006, 0x0ac4, 0x56d1, 0x1de5, 0x0007, 0x0a98, 0x56ab, 0x1e37, 0x0007,
|
|
0x0a6c, 0x5684, 0x1e89, 0x0008, 0x0a40, 0x565b, 0x1edc, 0x0009, 0x0a16, 0x5632, 0x1f2f, 0x0009, 0x09eb, 0x5609, 0x1f82, 0x000a,
|
|
0x09c1, 0x55de, 0x1fd6, 0x000b, 0x0998, 0x55b2, 0x202a, 0x000c, 0x096f, 0x5585, 0x207f, 0x000d, 0x0946, 0x5558, 0x20d4, 0x000e,
|
|
0x091e, 0x5529, 0x2129, 0x000f, 0x08f7, 0x54fa, 0x217f, 0x0010, 0x08d0, 0x54ca, 0x21d5, 0x0011, 0x08a9, 0x5499, 0x222c, 0x0012,
|
|
0x0883, 0x5467, 0x2282, 0x0013, 0x085d, 0x5434, 0x22da, 0x0015, 0x0838, 0x5401, 0x2331, 0x0016, 0x0813, 0x53cc, 0x2389, 0x0018,
|
|
0x07ef, 0x5397, 0x23e1, 0x0019, 0x07cb, 0x5361, 0x2439, 0x001b, 0x07a7, 0x532a, 0x2492, 0x001c, 0x0784, 0x52f3, 0x24eb, 0x001e,
|
|
0x0762, 0x52ba, 0x2545, 0x0020, 0x0740, 0x5281, 0x259e, 0x0021, 0x071e, 0x5247, 0x25f8, 0x0023, 0x06fd, 0x520c, 0x2653, 0x0025,
|
|
0x06dc, 0x51d0, 0x26ad, 0x0027, 0x06bb, 0x5194, 0x2708, 0x0029, 0x069b, 0x5156, 0x2763, 0x002c, 0x067c, 0x5118, 0x27be, 0x002e,
|
|
0x065c, 0x50da, 0x281a, 0x0030, 0x063e, 0x509a, 0x2876, 0x0033, 0x061f, 0x505a, 0x28d2, 0x0035, 0x0601, 0x5019, 0x292e, 0x0038,
|
|
0x05e4, 0x4fd7, 0x298b, 0x003a, 0x05c7, 0x4f95, 0x29e7, 0x003d, 0x05aa, 0x4f52, 0x2a44, 0x0040, 0x058e, 0x4f0e, 0x2aa1, 0x0043,
|
|
0x0572, 0x4ec9, 0x2aff, 0x0046, 0x0556, 0x4e84, 0x2b5c, 0x0049, 0x053b, 0x4e3e, 0x2bba, 0x004d, 0x0520, 0x4df7, 0x2c18, 0x0050,
|
|
0x0506, 0x4db0, 0x2c76, 0x0054, 0x04ec, 0x4d68, 0x2cd4, 0x0057, 0x04d2, 0x4d20, 0x2d33, 0x005b, 0x04b9, 0x4cd7, 0x2d91, 0x005f,
|
|
0x04a0, 0x4c8d, 0x2df0, 0x0063, 0x0488, 0x4c42, 0x2e4f, 0x0067, 0x0470, 0x4bf7, 0x2eae, 0x006b, 0x0458, 0x4bac, 0x2f0d, 0x006f,
|
|
0x0441, 0x4b5f, 0x2f6c, 0x0074, 0x042a, 0x4b13, 0x2fcc, 0x0078, 0x0413, 0x4ac5, 0x302b, 0x007d, 0x03fc, 0x4a77, 0x308b, 0x0082,
|
|
0x03e7, 0x4a29, 0x30ea, 0x0087, 0x03d1, 0x49d9, 0x314a, 0x008c, 0x03bc, 0x498a, 0x31aa, 0x0091, 0x03a7, 0x493a, 0x3209, 0x0096,
|
|
0x0392, 0x48e9, 0x3269, 0x009c, 0x037e, 0x4898, 0x32c9, 0x00a1, 0x036a, 0x4846, 0x3329, 0x00a7, 0x0356, 0x47f4, 0x3389, 0x00ad,
|
|
0x0343, 0x47a1, 0x33e9, 0x00b3, 0x0330, 0x474e, 0x3449, 0x00ba, 0x031d, 0x46fa, 0x34a9, 0x00c0, 0x030b, 0x46a6, 0x3509, 0x00c7,
|
|
0x02f9, 0x4651, 0x3569, 0x00cd, 0x02e7, 0x45fc, 0x35c9, 0x00d4, 0x02d6, 0x45a6, 0x3629, 0x00db, 0x02c4, 0x4550, 0x3689, 0x00e3,
|
|
0x02b4, 0x44fa, 0x36e8, 0x00ea, 0x02a3, 0x44a3, 0x3748, 0x00f2, 0x0293, 0x444c, 0x37a8, 0x00fa, 0x0283, 0x43f4, 0x3807, 0x0101,
|
|
0x0273, 0x439c, 0x3867, 0x010a, 0x0264, 0x4344, 0x38c6, 0x0112, 0x0255, 0x42eb, 0x3926, 0x011b, 0x0246, 0x4292, 0x3985, 0x0123,
|
|
0x0237, 0x4239, 0x39e4, 0x012c, 0x0229, 0x41df, 0x3a43, 0x0135, 0x021b, 0x4185, 0x3aa2, 0x013f, 0x020d, 0x412a, 0x3b00, 0x0148,
|
|
0x0200, 0x40d0, 0x3b5f, 0x0152, 0x01f2, 0x4074, 0x3bbd, 0x015c, 0x01e5, 0x4019, 0x3c1b, 0x0166, 0x01d9, 0x3fbd, 0x3c79, 0x0171,
|
|
0x01cc, 0x3f62, 0x3cd7, 0x017b, 0x01c0, 0x3f05, 0x3d35, 0x0186, 0x01b4, 0x3ea9, 0x3d92, 0x0191, 0x01a8, 0x3e4c, 0x3def, 0x019c,
|
|
0x019c, 0x3def, 0x3e4c, 0x01a8, 0x0191, 0x3d92, 0x3ea9, 0x01b4, 0x0186, 0x3d35, 0x3f05, 0x01c0, 0x017b, 0x3cd7, 0x3f62, 0x01cc,
|
|
0x0171, 0x3c79, 0x3fbd, 0x01d9, 0x0166, 0x3c1b, 0x4019, 0x01e5, 0x015c, 0x3bbd, 0x4074, 0x01f2, 0x0152, 0x3b5f, 0x40d0, 0x0200,
|
|
0x0148, 0x3b00, 0x412a, 0x020d, 0x013f, 0x3aa2, 0x4185, 0x021b, 0x0135, 0x3a43, 0x41df, 0x0229, 0x012c, 0x39e4, 0x4239, 0x0237,
|
|
0x0123, 0x3985, 0x4292, 0x0246, 0x011b, 0x3926, 0x42eb, 0x0255, 0x0112, 0x38c6, 0x4344, 0x0264, 0x010a, 0x3867, 0x439c, 0x0273,
|
|
0x0101, 0x3807, 0x43f4, 0x0283, 0x00fa, 0x37a8, 0x444c, 0x0293, 0x00f2, 0x3748, 0x44a3, 0x02a3, 0x00ea, 0x36e8, 0x44fa, 0x02b4,
|
|
0x00e3, 0x3689, 0x4550, 0x02c4, 0x00db, 0x3629, 0x45a6, 0x02d6, 0x00d4, 0x35c9, 0x45fc, 0x02e7, 0x00cd, 0x3569, 0x4651, 0x02f9,
|
|
0x00c7, 0x3509, 0x46a6, 0x030b, 0x00c0, 0x34a9, 0x46fa, 0x031d, 0x00ba, 0x3449, 0x474e, 0x0330, 0x00b3, 0x33e9, 0x47a1, 0x0343,
|
|
0x00ad, 0x3389, 0x47f4, 0x0356, 0x00a7, 0x3329, 0x4846, 0x036a, 0x00a1, 0x32c9, 0x4898, 0x037e, 0x009c, 0x3269, 0x48e9, 0x0392,
|
|
0x0096, 0x3209, 0x493a, 0x03a7, 0x0091, 0x31aa, 0x498a, 0x03bc, 0x008c, 0x314a, 0x49d9, 0x03d1, 0x0087, 0x30ea, 0x4a29, 0x03e7,
|
|
0x0082, 0x308b, 0x4a77, 0x03fc, 0x007d, 0x302b, 0x4ac5, 0x0413, 0x0078, 0x2fcc, 0x4b13, 0x042a, 0x0074, 0x2f6c, 0x4b5f, 0x0441,
|
|
0x006f, 0x2f0d, 0x4bac, 0x0458, 0x006b, 0x2eae, 0x4bf7, 0x0470, 0x0067, 0x2e4f, 0x4c42, 0x0488, 0x0063, 0x2df0, 0x4c8d, 0x04a0,
|
|
0x005f, 0x2d91, 0x4cd7, 0x04b9, 0x005b, 0x2d33, 0x4d20, 0x04d2, 0x0057, 0x2cd4, 0x4d68, 0x04ec, 0x0054, 0x2c76, 0x4db0, 0x0506,
|
|
0x0050, 0x2c18, 0x4df7, 0x0520, 0x004d, 0x2bba, 0x4e3e, 0x053b, 0x0049, 0x2b5c, 0x4e84, 0x0556, 0x0046, 0x2aff, 0x4ec9, 0x0572,
|
|
0x0043, 0x2aa1, 0x4f0e, 0x058e, 0x0040, 0x2a44, 0x4f52, 0x05aa, 0x003d, 0x29e7, 0x4f95, 0x05c7, 0x003a, 0x298b, 0x4fd7, 0x05e4,
|
|
0x0038, 0x292e, 0x5019, 0x0601, 0x0035, 0x28d2, 0x505a, 0x061f, 0x0033, 0x2876, 0x509a, 0x063e, 0x0030, 0x281a, 0x50da, 0x065c,
|
|
0x002e, 0x27be, 0x5118, 0x067c, 0x002c, 0x2763, 0x5156, 0x069b, 0x0029, 0x2708, 0x5194, 0x06bb, 0x0027, 0x26ad, 0x51d0, 0x06dc,
|
|
0x0025, 0x2653, 0x520c, 0x06fd, 0x0023, 0x25f8, 0x5247, 0x071e, 0x0021, 0x259e, 0x5281, 0x0740, 0x0020, 0x2545, 0x52ba, 0x0762,
|
|
0x001e, 0x24eb, 0x52f3, 0x0784, 0x001c, 0x2492, 0x532a, 0x07a7, 0x001b, 0x2439, 0x5361, 0x07cb, 0x0019, 0x23e1, 0x5397, 0x07ef,
|
|
0x0018, 0x2389, 0x53cc, 0x0813, 0x0016, 0x2331, 0x5401, 0x0838, 0x0015, 0x22da, 0x5434, 0x085d, 0x0013, 0x2282, 0x5467, 0x0883,
|
|
0x0012, 0x222c, 0x5499, 0x08a9, 0x0011, 0x21d5, 0x54ca, 0x08d0, 0x0010, 0x217f, 0x54fa, 0x08f7, 0x000f, 0x2129, 0x5529, 0x091e,
|
|
0x000e, 0x20d4, 0x5558, 0x0946, 0x000d, 0x207f, 0x5585, 0x096f, 0x000c, 0x202a, 0x55b2, 0x0998, 0x000b, 0x1fd6, 0x55de, 0x09c1,
|
|
0x000a, 0x1f82, 0x5609, 0x09eb, 0x0009, 0x1f2f, 0x5632, 0x0a16, 0x0009, 0x1edc, 0x565b, 0x0a40, 0x0008, 0x1e89, 0x5684, 0x0a6c,
|
|
0x0007, 0x1e37, 0x56ab, 0x0a98, 0x0007, 0x1de5, 0x56d1, 0x0ac4, 0x0006, 0x1d93, 0x56f6, 0x0af1, 0x0005, 0x1d42, 0x571b, 0x0b1e,
|
|
0x0005, 0x1cf1, 0x573e, 0x0b4c, 0x0004, 0x1ca1, 0x5761, 0x0b7a, 0x0004, 0x1c51, 0x5782, 0x0ba9, 0x0003, 0x1c02, 0x57a3, 0x0bd8,
|
|
0x0003, 0x1bb3, 0x57c3, 0x0c07, 0x0003, 0x1b64, 0x57e2, 0x0c38, 0x0002, 0x1b16, 0x57ff, 0x0c68, 0x0002, 0x1ac8, 0x581c, 0x0c99,
|
|
0x0002, 0x1a7b, 0x5838, 0x0ccb, 0x0001, 0x1a2e, 0x5853, 0x0cfd, 0x0001, 0x19e2, 0x586d, 0x0d30, 0x0001, 0x1996, 0x5886, 0x0d63,
|
|
0x0001, 0x194b, 0x589e, 0x0d97, 0x0000, 0x1900, 0x58b5, 0x0dcb, 0x0000, 0x18b5, 0x58cb, 0x0e00, 0x0000, 0x186b, 0x58e0, 0x0e35,
|
|
0x0000, 0x1821, 0x58f4, 0x0e6b, 0x0000, 0x17d8, 0x5907, 0x0ea1, 0x0000, 0x1790, 0x5919, 0x0ed7, 0x0000, 0x1747, 0x592a, 0x0f0f,
|
|
0xffff, 0x1700, 0x593a, 0x0f46, 0xffff, 0x16b9, 0x5949, 0x0f7f, 0xffff, 0x1672, 0x5958, 0x0fb7, 0xffff, 0x162c, 0x5965, 0x0ff1,
|
|
0xffff, 0x15e6, 0x5971, 0x102a, 0xffff, 0x15a0, 0x597c, 0x1065, 0xffff, 0x155c, 0x5986, 0x109f, 0xffff, 0x1517, 0x598f, 0x10db,
|
|
0xffff, 0x14d4, 0x5997, 0x1116, 0xffff, 0x1490, 0x599e, 0x1153, 0xffff, 0x144d, 0x59a4, 0x118f, 0xffff, 0x140b, 0x59a9, 0x11cd,
|
|
0xffff, 0x13c9, 0x59ad, 0x120b, 0xffff, 0x1388, 0x59b0, 0x1249, 0xffff, 0x1347, 0x59b2, 0x1288, 0xffff, 0x1307, 0x59b3, 0x12c7,
|
|
};
|
|
|
|
static sint32 ratelogtable[32+128];
|
|
|
|
/*
|
|
// v1.10 coefs - normalized from v1.04
|
|
static const sint32 reverb_lowpass_coefs[8] = {
|
|
(int)((-0.036346113709214548)*(2048.0)),
|
|
(int)(( 0.044484956332843419)*(2048.0)),
|
|
(int)(( 0.183815456609675380)*(2048.0)),
|
|
(int)(( 0.308045700766695740)*(2048.0)),
|
|
(int)(( 0.308045700766695740)*(2048.0)),
|
|
(int)(( 0.183815456609675380)*(2048.0)),
|
|
(int)(( 0.044484956332843419)*(2048.0)),
|
|
(int)((-0.036346113709214548)*(2048.0))
|
|
};
|
|
*/
|
|
|
|
/*
|
|
// test coefs - ganked from LAME's blackman function
|
|
// (11-point filter, but only a few points were nonzero)
|
|
static const sint32 reverb_new_lowpass_coefs[3] = {
|
|
(int)((-0.02134438446523164)*(2048.0)),
|
|
// skip one
|
|
(int)(( 0.27085135668587779)*(2048.0)),
|
|
(int)(( 0.50098605555870768)*(2048.0))
|
|
// rest are symmetrical
|
|
};
|
|
*/
|
|
|
|
// PS1 reverb downsampling coefficients(as best as I could extract them at the moment, some of the even(non-zero/non-16384) ones *might* be off by 1.
|
|
// -------------
|
|
#if 1
|
|
static const sint32 reverb_psx_lowpass_coefs[11] = {
|
|
-1,
|
|
// 0,
|
|
2,
|
|
// 0,
|
|
-10,
|
|
// 0,
|
|
35,
|
|
// 0,
|
|
-103,
|
|
// 0,
|
|
266,
|
|
// 0,
|
|
-616,
|
|
// 0,
|
|
1332,
|
|
// 0,
|
|
-2960,
|
|
// 0,
|
|
10246,
|
|
16384
|
|
};
|
|
#else
|
|
static const sint32 reverb_psx_lowpass_coefs[48] = {
|
|
-1,
|
|
0,
|
|
2,
|
|
0,
|
|
-10,
|
|
0,
|
|
35,
|
|
0,
|
|
-103,
|
|
0,
|
|
266,
|
|
0,
|
|
-616,
|
|
0,
|
|
1332,
|
|
0,
|
|
-2960,
|
|
0,
|
|
10246,
|
|
16384,
|
|
10246,
|
|
0,
|
|
-2960,
|
|
0,
|
|
1332,
|
|
0,
|
|
-616,
|
|
0,
|
|
266,
|
|
0,
|
|
-103,
|
|
0,
|
|
35,
|
|
0,
|
|
-10,
|
|
0,
|
|
2,
|
|
0,
|
|
-1,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
};
|
|
#endif
|
|
|
|
|
|
static const int noisetable[] = {
|
|
1, 0, 0, 1, 0, 1, 1, 0,
|
|
1, 0, 0, 1, 0, 1, 1, 0,
|
|
1, 0, 0, 1, 0, 1, 1, 0,
|
|
1, 0, 0, 1, 0, 1, 1, 0,
|
|
0, 1, 1, 0, 1, 0, 0, 1,
|
|
0, 1, 1, 0, 1, 0, 0, 1,
|
|
0, 1, 1, 0, 1, 0, 0, 1,
|
|
0, 1, 1, 0, 1, 0, 0, 1
|
|
};
|
|
|
|
/*
|
|
** Static init
|
|
*/
|
|
sint32 EMU_CALL spucore_init(void) {
|
|
sint32 i;
|
|
|
|
memset(ratelogtable, 0, sizeof(ratelogtable));
|
|
ratelogtable[32-8] = 1;
|
|
ratelogtable[32-7] = 1;
|
|
ratelogtable[32-6] = 1;
|
|
ratelogtable[32-5] = 1;
|
|
ratelogtable[32-4] = 2;
|
|
ratelogtable[32-3] = 2;
|
|
ratelogtable[32-2] = 3;
|
|
ratelogtable[32-1] = 3;
|
|
ratelogtable[32+0] = 4;
|
|
ratelogtable[32+1] = 5;
|
|
ratelogtable[32+2] = 6;
|
|
ratelogtable[32+3] = 7;
|
|
for(i = 4; i < 128; i++) {
|
|
uint32 n = 2*ratelogtable[32+i-4];
|
|
if(n > 0x20000000) n = 0x20000000;
|
|
ratelogtable[32+i] = n;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** State information
|
|
*/
|
|
#define SPUCORESTATE ((struct SPUCORE_STATE*)(state))
|
|
|
|
#define SAMPLE_STATE_OFF (0)
|
|
#define SAMPLE_STATE_ENDING (1)
|
|
#define SAMPLE_STATE_ON (2)
|
|
|
|
struct SPUCORE_SAMPLE {
|
|
uint8 state;
|
|
uint8 array_cleared;
|
|
sint32 array[32];
|
|
uint32 phase;
|
|
|
|
uint32 block_addr;
|
|
uint32 start_block_addr;
|
|
//uint32 start_loop_block_addr;
|
|
uint32 loop_block_addr;
|
|
};
|
|
|
|
#define ENVELOPE_STATE_OFF (0)
|
|
#define ENVELOPE_STATE_ATTACK (1)
|
|
#define ENVELOPE_STATE_DECAY (2)
|
|
#define ENVELOPE_STATE_SUSTAIN (3)
|
|
#define ENVELOPE_STATE_RELEASE (4)
|
|
|
|
struct SPUCORE_ENVELOPE {
|
|
uint32 reg_ad;
|
|
uint32 reg_sr;
|
|
sint32 level;
|
|
sint32 delta;
|
|
int state;
|
|
sint32 cachemax;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** Volumes with increase/decrease modes
|
|
*/
|
|
struct SPUCORE_VOLUME {
|
|
uint16 mode;
|
|
sint32 level;
|
|
};
|
|
|
|
static EMU_INLINE void EMU_CALL volume_setmode(struct SPUCORE_VOLUME *vol, uint16 mode) {
|
|
vol->mode = mode;
|
|
if(mode & 0x8000) {
|
|
} else {
|
|
vol->level = mode;
|
|
vol->level <<= 17;
|
|
vol->level >>= 1;
|
|
}
|
|
}
|
|
|
|
static EMU_INLINE uint16 EMU_CALL volume_getmode(struct SPUCORE_VOLUME *vol) {
|
|
return vol->mode;
|
|
}
|
|
|
|
static EMU_INLINE sint32 EMU_CALL volume_getlevel(struct SPUCORE_VOLUME *vol) {
|
|
return (vol->level) >> 15;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct SPUCORE_CHAN {
|
|
struct SPUCORE_VOLUME vol[2];
|
|
uint32 voice_pitch;
|
|
struct SPUCORE_SAMPLE sample;
|
|
struct SPUCORE_ENVELOPE env;
|
|
int samples_until_pending_keyon;
|
|
int samples_until_keyoff_responds;
|
|
};
|
|
|
|
/*
|
|
** Reverb resample state
|
|
*/
|
|
struct SPUCORE_REVERB_RESAMPLER {
|
|
sint32 in_queue_l[64];
|
|
sint32 in_queue_r[64];
|
|
sint32 out_queue_l[16];
|
|
sint32 out_queue_r[16];
|
|
int queue_index;
|
|
};
|
|
|
|
struct SPUCORE_REVERB {
|
|
uint32 FB_SRC_A ;
|
|
uint32 FB_SRC_B ;
|
|
uint16 IIR_ALPHA ;
|
|
uint16 ACC_COEF_A ;
|
|
uint16 ACC_COEF_B ;
|
|
uint16 ACC_COEF_C ;
|
|
uint16 ACC_COEF_D ;
|
|
uint16 IIR_COEF ;
|
|
uint16 FB_ALPHA ;
|
|
uint16 FB_X ;
|
|
uint32 IIR_DEST_A0;
|
|
uint32 IIR_DEST_A1;
|
|
uint32 ACC_SRC_A0 ;
|
|
uint32 ACC_SRC_A1 ;
|
|
uint32 ACC_SRC_B0 ;
|
|
uint32 ACC_SRC_B1 ;
|
|
uint32 IIR_SRC_A0 ;
|
|
uint32 IIR_SRC_A1 ;
|
|
uint32 IIR_DEST_B0;
|
|
uint32 IIR_DEST_B1;
|
|
uint32 ACC_SRC_C0 ;
|
|
uint32 ACC_SRC_C1 ;
|
|
uint32 ACC_SRC_D0 ;
|
|
uint32 ACC_SRC_D1 ;
|
|
uint32 IIR_SRC_B1 ;
|
|
uint32 IIR_SRC_B0 ;
|
|
uint32 MIX_DEST_A0;
|
|
uint32 MIX_DEST_A1;
|
|
uint32 MIX_DEST_B0;
|
|
uint32 MIX_DEST_B1;
|
|
uint16 IN_COEF_L ;
|
|
uint16 IN_COEF_R ;
|
|
|
|
uint32 start_address;
|
|
uint32 end_address;
|
|
|
|
sint32 current_address;
|
|
|
|
sint32 safe_start_address;
|
|
sint32 safe_end_address;
|
|
sint32 safe_size;
|
|
|
|
struct SPUCORE_REVERB_RESAMPLER resampler;
|
|
};
|
|
|
|
struct SPUCORE_STATE {
|
|
uint32 flags;
|
|
sint32 memsize;
|
|
struct SPUCORE_CHAN chan[24];
|
|
struct SPUCORE_REVERB reverb;
|
|
struct SPUCORE_VOLUME mvol[2];
|
|
sint16 evol[2];
|
|
sint16 avol[2];
|
|
sint16 bvol[2];
|
|
uint32 kon;
|
|
uint32 koff;
|
|
uint32 fm;
|
|
uint32 noise;
|
|
uint32 vmix[2];
|
|
uint32 vmixe[2];
|
|
uint32 irq_address;
|
|
uint32 noiseclock;
|
|
uint32 noisecounter;
|
|
sint32 noiseval;
|
|
uint32 irq_decoder_clock;
|
|
uint32 irq_triggered_cycle;
|
|
};
|
|
|
|
struct SPUCORE_IRQ_STATE {
|
|
uint32 offset;
|
|
uint32 triggered_cycle;
|
|
};
|
|
|
|
uint32 EMU_CALL spucore_get_state_size(void) {
|
|
return(sizeof(struct SPUCORE_STATE));
|
|
}
|
|
|
|
/*
|
|
** Initialize SPU CORE state
|
|
*/
|
|
void EMU_CALL spucore_clear_state(void *state) {
|
|
/*
|
|
** Clear to zero
|
|
*/
|
|
memset(state, 0, sizeof(struct SPUCORE_STATE));
|
|
/*
|
|
** Set other defaults
|
|
*/
|
|
SPUCORESTATE->memsize = 0x80000;
|
|
spucore_setreg(state, SPUREG_EEA, 0x7FFFF, 0xFFFFFFFF);
|
|
spucore_setreg(state, SPUREG_VMIX, 0x00FFFFFF, 0xFFFFFFFF);
|
|
spucore_setflag(state, SPUREG_FLAG_MSNDL , 1);
|
|
spucore_setflag(state, SPUREG_FLAG_MSNDR , 1);
|
|
spucore_setflag(state, SPUREG_FLAG_MSNDEL, 1);
|
|
spucore_setflag(state, SPUREG_FLAG_MSNDER, 1);
|
|
spucore_setflag(state, SPUREG_FLAG_SINL, 1);
|
|
spucore_setflag(state, SPUREG_FLAG_SINR, 1);
|
|
SPUCORESTATE->irq_triggered_cycle = 0xFFFFFFFF;
|
|
}
|
|
|
|
void EMU_CALL spucore_set_mem_size(void *state, uint32 size) {
|
|
SPUCORESTATE->memsize = (sint32)size;
|
|
spucore_setreg(state, SPUREG_EEA, size-1, 0xFFFFFFFF);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void EMU_CALL make_safe_reverb_addresses(struct SPUCORE_STATE *state) {
|
|
struct SPUCORE_REVERB *r = &(state->reverb);
|
|
sint32 sa = r->start_address;
|
|
sint32 ea = r->end_address;
|
|
|
|
//EMUTRACE2("[sa,ea=%X,%X]\n",sa,ea);
|
|
|
|
ea += 0x20000;
|
|
ea &= (~0x1FFFF);
|
|
sa &= (~1);
|
|
|
|
if(ea > state->memsize) ea = state->memsize;
|
|
if(ea < 0x20000) ea = 0x20000;
|
|
if(sa > ea) {
|
|
sa &= 0x1FFFE;
|
|
sa += ea;
|
|
sa -= 0x20000;
|
|
}
|
|
|
|
r->safe_start_address = sa;
|
|
r->safe_end_address = ea;
|
|
r->safe_size = ea-sa;
|
|
|
|
r->current_address &= (~1);
|
|
if(
|
|
(r->current_address < r->safe_start_address) ||
|
|
(r->current_address >= r->safe_end_address)
|
|
) {
|
|
r->current_address = r->safe_start_address;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** ADPCM sample decoding
|
|
*/
|
|
/*
|
|
#define SPUCORE_PREDICT_SKEL(prednum,coef1,coef2) \
|
|
static void EMU_CALL spucore_predict_##prednum(uint8 *src, sint32 *dest, uint32 shift) { \
|
|
uint32 i; \
|
|
sint32 p_a = dest[-2]; \
|
|
sint32 p_b = dest[-1]; \
|
|
shift += 16; \
|
|
for(i = 0; i < 14; i++) { \
|
|
sint32 a = src[i^(EMU_ENDIAN_XOR(1))]; \
|
|
sint32 b = (a&0xF0)<<24; \
|
|
a = a << 28; b >>= shift; a >>= shift; \
|
|
a += ( ( ((coef1)*p_b) + ((coef2)*p_a) )+32)>>6; \
|
|
if(a > 32767) { a = 32767; } if(a < (-32768)) { a = (-32768); } \
|
|
*dest++ = a; \
|
|
b += ( ( ((coef1)* a ) + ((coef2)*p_b) )+32)>>6; \
|
|
if(b > 32767) { b = 32767; } if(b < (-32768)) { b = (-32768); } \
|
|
*dest++ = b; \
|
|
p_a = a; p_b = b; \
|
|
} \
|
|
}
|
|
*/
|
|
|
|
#define SPUCORE_PREDICT_SKEL(prednum,coef1,coef2) \
|
|
static void EMU_CALL spucore_predict_##prednum(uint16 *src, sint32 *dest, uint32 shift) { \
|
|
uint32 i; \
|
|
sint32 p_a = dest[-2]; \
|
|
sint32 p_b = dest[-1]; \
|
|
shift += 16; \
|
|
for(i = 0; i < 7; i++) { \
|
|
sint32 a = *src++; \
|
|
sint32 b = (a&0x00F0)<<24; \
|
|
sint32 c = (a&0x0F00)<<20; \
|
|
sint32 d = (a&0xF000)<<16; \
|
|
a <<= 28; \
|
|
a >>= shift; \
|
|
b >>= shift; \
|
|
c >>= shift; \
|
|
d >>= shift; \
|
|
a += ( ( ((coef1)*p_b) + ((coef2)*p_a) )+32)>>6; \
|
|
if(a > 32767) { a = 32767; } if(a < (-32768)) { a = (-32768); } \
|
|
*dest++ = a; \
|
|
b += ( ( ((coef1)* a ) + ((coef2)*p_b) )+32)>>6; \
|
|
if(b > 32767) { b = 32767; } if(b < (-32768)) { b = (-32768); } \
|
|
*dest++ = b; \
|
|
c += ( ( ((coef1)* b ) + ((coef2)* a ) )+32)>>6; \
|
|
if(b > 32767) { b = 32767; } if(b < (-32768)) { b = (-32768); } \
|
|
*dest++ = c; \
|
|
d += ( ( ((coef1)* c ) + ((coef2)* b ) )+32)>>6; \
|
|
if(b > 32767) { b = 32767; } if(b < (-32768)) { b = (-32768); } \
|
|
*dest++ = d; \
|
|
p_a = c; p_b = d; \
|
|
} \
|
|
}
|
|
|
|
SPUCORE_PREDICT_SKEL(0,0,0)
|
|
SPUCORE_PREDICT_SKEL(1,60,0)
|
|
SPUCORE_PREDICT_SKEL(2,115,-52)
|
|
SPUCORE_PREDICT_SKEL(3,98,-55)
|
|
SPUCORE_PREDICT_SKEL(4,122,-60)
|
|
|
|
typedef void (EMU_CALL * spucore_predict_callback_t)(uint16*, sint32*, uint32);
|
|
|
|
static spucore_predict_callback_t spucore_predict[8] = {
|
|
spucore_predict_0, spucore_predict_1, spucore_predict_2, spucore_predict_3,
|
|
spucore_predict_4, spucore_predict_1, spucore_predict_2, spucore_predict_3
|
|
};
|
|
|
|
static void EMU_CALL decode_sample_block(
|
|
uint16 *ram,
|
|
uint32 memmax,
|
|
struct SPUCORE_SAMPLE *sample,
|
|
int skip
|
|
) {
|
|
// uint32 i;
|
|
if(sample->state != SAMPLE_STATE_ON) {
|
|
if(!sample->array_cleared) {
|
|
memset(sample->array, 0, sizeof(sample->array));
|
|
sample->array_cleared = 1;
|
|
}
|
|
sample->state = SAMPLE_STATE_OFF;
|
|
} else {
|
|
/* temporary hack to avoid memory problems */
|
|
sample->block_addr &= (memmax-1);
|
|
if((sample->block_addr + 0x10) > memmax) sample->block_addr -= 0x10;
|
|
ram += sample->block_addr >> 1;
|
|
/* decode */
|
|
if(skip) {
|
|
if(!sample->array_cleared) {
|
|
memset(sample->array, 0, sizeof(sample->array));
|
|
sample->array_cleared = 1;
|
|
}
|
|
} else {
|
|
sample->array[0] = sample->array[28];
|
|
sample->array[1] = sample->array[29];
|
|
sample->array[2] = sample->array[30];
|
|
sample->array[3] = sample->array[31];
|
|
(spucore_predict[(ram[0]>>4)&7])(
|
|
ram + 1,
|
|
sample->array + 4,
|
|
ram[0] & 0xF
|
|
);
|
|
}
|
|
/* set loop start address if necessary */
|
|
if(ram[0]&0x0400) {
|
|
sample->loop_block_addr = sample->block_addr;
|
|
}
|
|
/* sample end? */
|
|
if(ram[0]&0x0100) {
|
|
/* loop? */
|
|
if(ram[0]&0x0200) {
|
|
sample->block_addr = sample->loop_block_addr;
|
|
/* no loop, just end */
|
|
} else {
|
|
sample->state = SAMPLE_STATE_ENDING;
|
|
}
|
|
} else {
|
|
/* advance to next block */
|
|
sample->block_addr += 16;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Returns the number of samples actually generated
|
|
// If dest is null, it means "fast forward" - don't render anything
|
|
//
|
|
//
|
|
// output of resampler() is guaranteed clipped, as long as the output of
|
|
// decode_sample_block() is
|
|
//
|
|
static uint32 EMU_CALL resampler(
|
|
uint16 *ram,
|
|
uint32 memmax,
|
|
struct SPUCORE_SAMPLE *sample,
|
|
sint32 *dest,
|
|
uint32 n,
|
|
uint32 phase_inc,
|
|
struct SPUCORE_IRQ_STATE *irq_state
|
|
) {
|
|
uint32 irq_triggered_cycle = 0xFFFFFFFF;
|
|
uint32 s;
|
|
uint32 ph; //, phl;
|
|
ph = sample->phase;
|
|
if(!dest) {
|
|
ph += phase_inc * n;
|
|
s = 0;
|
|
while(ph >= 0x1C000) {
|
|
if(sample->state == SAMPLE_STATE_OFF) break;
|
|
if(irq_state && irq_state->offset - sample->block_addr < 16 && irq_triggered_cycle == 0xFFFFFFFF) {
|
|
irq_triggered_cycle = s;
|
|
}
|
|
decode_sample_block(ram, memmax, sample, 1);
|
|
s += phase_inc * 28;
|
|
ph -= 0x1C000;
|
|
}
|
|
s = n;
|
|
} else {
|
|
uint32 t = 0;
|
|
for(s = 0; s < n; s++) {
|
|
sint32 *source_signal;
|
|
sint16 *mygauss;
|
|
sint32 sum;
|
|
if(ph >= 0x1C000) {
|
|
if(sample->state == SAMPLE_STATE_OFF) break;
|
|
if(irq_state && irq_state->offset - sample->block_addr < 16 && irq_triggered_cycle == 0xFFFFFFFF) {
|
|
irq_triggered_cycle = t;
|
|
}
|
|
decode_sample_block(ram, memmax, sample, 0);
|
|
ph -= 0x1C000;
|
|
}
|
|
source_signal = sample->array + (ph >> 12);
|
|
mygauss = (sint16*) (((uint8*)gauss_shuffled_reverse_table) + ((ph & 0xFF0) >> 1));
|
|
|
|
{ sum =
|
|
(source_signal[0] * mygauss[0]) +
|
|
(source_signal[1] * mygauss[1]) +
|
|
(source_signal[2] * mygauss[2]) +
|
|
(source_signal[3] * mygauss[3]);
|
|
}
|
|
sum >>= 15;
|
|
|
|
*dest++ = sum;
|
|
ph += phase_inc;
|
|
t += phase_inc;
|
|
}
|
|
}
|
|
|
|
if(irq_state && irq_triggered_cycle != 0xFFFFFFFF) {
|
|
irq_triggered_cycle = (irq_triggered_cycle * 768) >> 12;
|
|
if(irq_triggered_cycle < irq_state->triggered_cycle) irq_state->triggered_cycle = irq_triggered_cycle;
|
|
}
|
|
|
|
sample->phase = ph;
|
|
|
|
return s;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static uint32 EMU_CALL resampler_modulated(
|
|
uint16 *ram,
|
|
uint32 memmax,
|
|
struct SPUCORE_SAMPLE *sample,
|
|
sint32 *dest,
|
|
uint32 n,
|
|
uint32 phase_inc,
|
|
sint32 *fmbuf,
|
|
struct SPUCORE_IRQ_STATE *irq_state
|
|
) {
|
|
uint32 irq_triggered_cycle = 0xFFFFFFFF;
|
|
uint32 s, t = 0;
|
|
uint32 ph; //, phl;
|
|
uint32 pimod;
|
|
ph = sample->phase;
|
|
if(!dest) {
|
|
for(s = 0; s < n; s++) {
|
|
pimod = ((*fmbuf++ + 32768) * phase_inc) / 32768;
|
|
if(pimod > 0x3FFF) pimod = 0x3FFF;
|
|
else if(pimod < 1) pimod = 1;
|
|
ph += pimod;
|
|
while(ph >= 0x1C000) {
|
|
if(sample->state == SAMPLE_STATE_OFF) break;
|
|
if(irq_state && irq_state->offset - sample->block_addr < 16 && irq_triggered_cycle == 0xFFFFFFFF) {
|
|
irq_triggered_cycle = t;
|
|
}
|
|
decode_sample_block(ram, memmax, sample, 1);
|
|
ph -= 0x1C000;
|
|
}
|
|
t += pimod;
|
|
}
|
|
} else {
|
|
for(s = 0; s < n; s++) {
|
|
sint32 *source_signal;
|
|
sint16 *mygauss;
|
|
sint32 sum;
|
|
if(ph >= 0x1C000) {
|
|
if(sample->state == SAMPLE_STATE_OFF) break;
|
|
if(irq_state && irq_state->offset - sample->block_addr < 16 && irq_triggered_cycle == 0xFFFFFFFF) {
|
|
irq_triggered_cycle = t;
|
|
}
|
|
decode_sample_block(ram, memmax, sample, 0);
|
|
ph -= 0x1C000;
|
|
}
|
|
source_signal = sample->array + (ph >> 12);
|
|
mygauss = (sint16*) (((uint8*)gauss_shuffled_reverse_table) + ((ph & 0xFF0) >> 1));
|
|
|
|
{ sum =
|
|
(source_signal[0] * mygauss[0]) +
|
|
(source_signal[1] * mygauss[1]) +
|
|
(source_signal[2] * mygauss[2]) +
|
|
(source_signal[3] * mygauss[3]);
|
|
}
|
|
sum >>= 15;
|
|
|
|
*dest++ = sum;
|
|
pimod = ((*fmbuf++ + 32768) * phase_inc) / 32768;
|
|
if(pimod > 0x3FFF) pimod = 0x3FFF;
|
|
else if(pimod < 1) pimod = 1;
|
|
ph += pimod;
|
|
t += pimod;
|
|
}
|
|
}
|
|
|
|
if(irq_state && irq_triggered_cycle != 0xFFFFFFFF) {
|
|
irq_triggered_cycle = (irq_triggered_cycle * 768) >> 12;
|
|
if(irq_triggered_cycle < irq_state->triggered_cycle) irq_state->triggered_cycle = irq_triggered_cycle;
|
|
}
|
|
|
|
sample->phase = ph;
|
|
|
|
return s;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MY_AM (((env->reg_ad)>>15)&0x01)
|
|
#define MY_AR (((env->reg_ad)>> 8)&0x7F)
|
|
#define MY_DR (((env->reg_ad)>> 4)&0x0F)
|
|
#define MY_SL (((env->reg_ad)>> 0)&0x0F)
|
|
#define MY_SM (((env->reg_sr)>>15)&0x01)
|
|
#define MY_SD (((env->reg_sr)>>14)&0x01)
|
|
#define MY_SR (((env->reg_sr)>> 6)&0x7F)
|
|
#define MY_RM (((env->reg_sr)>> 5)&0x01)
|
|
#define MY_RR (((env->reg_sr)>> 0)&0x1F)
|
|
|
|
/*
|
|
** - Sets the current envelope slope
|
|
** - Returns the max number of samples that can be processed at the current
|
|
** slope
|
|
*/
|
|
static EMU_INLINE sint32 EMU_CALL envelope_do(struct SPUCORE_ENVELOPE *env) {
|
|
sint32 target = 0;
|
|
/*
|
|
** Clip envelope value in case it wrapped around
|
|
*/
|
|
switch(((env->level) >> 30) & 3) {
|
|
case 2: env->level = 0x7FFFFFFF; break;
|
|
case 3: env->level = 0x00000000; break;
|
|
}
|
|
switch(env->state) {
|
|
case ENVELOPE_STATE_ATTACK : goto attack;
|
|
case ENVELOPE_STATE_DECAY : goto decay;
|
|
case ENVELOPE_STATE_SUSTAIN: goto sustain;
|
|
case ENVELOPE_STATE_RELEASE: goto release;
|
|
default:
|
|
env->state = ENVELOPE_STATE_OFF;
|
|
env->level = 0;
|
|
env->delta = 0;
|
|
return 1;
|
|
}
|
|
attack:
|
|
if(env->level == 0x7FFFFFFF) {
|
|
env->state = ENVELOPE_STATE_DECAY;
|
|
goto decay;
|
|
}
|
|
/* log */
|
|
if(MY_AM) {
|
|
if(env->level < 0x60000000) {
|
|
target = 0x60000000;
|
|
env->delta = ratelogtable[32+(MY_AR^0x7F)-0x10];
|
|
} else {
|
|
target = 0x7FFFFFFF;
|
|
env->delta = ratelogtable[32+(MY_AR^0x7F)-0x18];
|
|
}
|
|
/* linear */
|
|
} else {
|
|
target = 0x7FFFFFFF;
|
|
env->delta = ratelogtable[32+(MY_AR^0x7F)-0x10];
|
|
}
|
|
goto domax;
|
|
|
|
decay:
|
|
if((((env->level)>>27)&0xF) <= ((sint32)(MY_SL))) {
|
|
env->state = ENVELOPE_STATE_SUSTAIN;
|
|
goto sustain;
|
|
}
|
|
target = env->level & (~0x07FFFFFF);
|
|
switch(((env->level)>>28)&0x7) {
|
|
case 0: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+0]; break;
|
|
case 1: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+4]; break;
|
|
case 2: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+6]; break;
|
|
case 3: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+8]; break;
|
|
case 4: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+9]; break;
|
|
case 5: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+10]; break;
|
|
case 6: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+11]; break;
|
|
case 7: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+12]; break;
|
|
}
|
|
goto domax;
|
|
|
|
sustain:
|
|
if(!MY_SD) {
|
|
if(env->level == 0x7FFFFFFF) {
|
|
env->delta = 0;
|
|
return 0x7FFFFFFF;
|
|
}
|
|
/* log */
|
|
if(MY_SM) {
|
|
if(env->level < 0x60000000) {
|
|
target = 0x60000000;
|
|
env->delta = ratelogtable[32+(MY_SR^0x7F)-0x10];
|
|
} else {
|
|
target = 0x7FFFFFFF;
|
|
env->delta = ratelogtable[32+(MY_SR^0x7F)-0x18];
|
|
}
|
|
/* linear */
|
|
} else {
|
|
target = 0x7FFFFFFF;
|
|
env->delta = ratelogtable[32+(MY_SR^0x7F)-0x10];
|
|
}
|
|
} else {
|
|
if(env->level == 0x00000000) {
|
|
env->delta = 0;
|
|
return 0x7FFFFFFF;
|
|
}
|
|
/* log */
|
|
if(MY_SM) {
|
|
target = ((env->level)&(~0x0FFFFFFF));
|
|
switch(((env->level)>>28)&0x7) {
|
|
case 0: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+0]; break;
|
|
case 1: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+4]; break;
|
|
case 2: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+6]; break;
|
|
case 3: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+8]; break;
|
|
case 4: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+9]; break;
|
|
case 5: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+10]; break;
|
|
case 6: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+11]; break;
|
|
case 7: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+12]; break;
|
|
}
|
|
/* linear */
|
|
} else {
|
|
target = 0x00000000;
|
|
env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x0F];
|
|
}
|
|
}
|
|
goto domax;
|
|
|
|
release:
|
|
if(env->level == 0) {
|
|
env->delta = 0;
|
|
env->state = ENVELOPE_STATE_OFF;
|
|
return 1;
|
|
}
|
|
/* log */
|
|
if(MY_RM) {
|
|
target = ((env->level) & (~0x0FFFFFFF));
|
|
switch(((env->level)>>28)&0x7) {
|
|
case 0: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+0]; break;
|
|
case 1: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+4]; break;
|
|
case 2: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+6]; break;
|
|
case 3: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+8]; break;
|
|
case 4: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+9]; break;
|
|
case 5: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+10]; break;
|
|
case 6: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+11]; break;
|
|
case 7: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+12]; break;
|
|
}
|
|
/* linear */
|
|
} else {
|
|
target = 0;
|
|
env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x0C];
|
|
}
|
|
goto domax;
|
|
|
|
domax:
|
|
{ sint32 max;
|
|
if(env->delta) {
|
|
max = (target - (env->level)) / (env->delta);
|
|
} else {
|
|
max = 0x7FFFFFFF;
|
|
}
|
|
max--;
|
|
if(max < 1) max = 1;
|
|
return max;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void EMU_CALL envelope_prime(struct SPUCORE_ENVELOPE *env) {
|
|
// if(env->state != ENVELOPE_STATE_OFF) {
|
|
//if(env->state!=ENVELOPE_STATE_RELEASE){
|
|
// OutputDebugString("envelope re-prime\n");
|
|
//}
|
|
// }
|
|
|
|
//EMUTRACE2("[eprime %04X %04X]",env->reg_ad_x,env->reg_sr_x);
|
|
|
|
env->level = 1;
|
|
env->state = ENVELOPE_STATE_ATTACK;
|
|
env->delta = 1;
|
|
|
|
env->cachemax = 0;
|
|
}
|
|
|
|
static void EMU_CALL envelope_release(struct SPUCORE_ENVELOPE *env) {
|
|
if(env->state != ENVELOPE_STATE_OFF) {
|
|
env->state = ENVELOPE_STATE_RELEASE;
|
|
}
|
|
env->cachemax = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void EMU_CALL sample_prime(struct SPUCORE_SAMPLE *sample) {
|
|
sample->state = SAMPLE_STATE_ON;
|
|
memset(sample->array, 0, sizeof(sample->array));
|
|
sample->array_cleared = 1;
|
|
sample->phase = 0x1C000; // ensure it grabs the first block right away
|
|
//EMUTRACE2("[sprime %08X %08X]", sample->start_block_addr, sample->start_loop_block_addr);
|
|
|
|
sample->block_addr = sample->start_block_addr;
|
|
//sample->loop_block_addr = sample->start_loop_block_addr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void EMU_CALL voice_on(struct SPUCORE_CHAN *c) {
|
|
|
|
// FOR DEBUGGING PURPOSES ONLY.
|
|
// if(c->sample.state == SAMPLE_STATE_ON)return;
|
|
// if(c->env.state != ENVELOPE_STATE_OFF)return;
|
|
/*
|
|
** Defer if already on
|
|
*/
|
|
c->samples_until_keyoff_responds = KEYON_KEYOFF_BLOCK_SAMPLES;
|
|
if(c->env.state != ENVELOPE_STATE_OFF) {
|
|
//EMUTRACE0("alreadyon:");
|
|
if(!(c->samples_until_pending_keyon)) {
|
|
//EMUTRACE0("defer");
|
|
c->samples_until_pending_keyon = KEYON_DEFER_SAMPLES;
|
|
} else {
|
|
//EMUTRACE0("was already deferred");
|
|
}
|
|
} else {
|
|
// EMUTRACE0("prime");
|
|
sample_prime(&(c->sample));
|
|
envelope_prime(&(c->env));
|
|
}
|
|
// EMUTRACE0("\n");
|
|
}
|
|
|
|
static void EMU_CALL voice_off(struct SPUCORE_CHAN *c) {
|
|
//EMUTRACE0("release");
|
|
if (!(c->samples_until_keyoff_responds)) {
|
|
envelope_release(&(c->env));
|
|
}
|
|
//EMUTRACE0("\n");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void EMU_CALL voices_on(struct SPUCORE_STATE *state, uint32 bits) {
|
|
int a;
|
|
for(a = 0; a < 24; a++) {
|
|
if(bits & 1) {
|
|
|
|
// EMUTRACE1("voiceon %2d:",a);
|
|
|
|
//EMUTRACE2("[vol %08X %08X]",state->chan[a].vol[0].level,state->chan[a].vol[1].level);
|
|
//EMUTRACE2("[mvol %08X %08X]",state->mvol[0].level,state->mvol[1].level);
|
|
////EMUTRACE2("[avol %08X %08X]",state->avol[0],state->avol[1]);
|
|
//EMUTRACE2("[evol %08X %08X]",state->evol[0],state->evol[1]);
|
|
|
|
// sint32 extvol_l = ((sint16)(state->avol[0]));
|
|
// sint32 extvol_r = ((sint16)(state->avol[1]));
|
|
|
|
voice_on(state->chan + a);
|
|
}
|
|
bits >>= 1;
|
|
}
|
|
}
|
|
|
|
static void EMU_CALL voices_off(struct SPUCORE_STATE *state, uint32 bits) {
|
|
int a;
|
|
for(a = 0; a < 24; a++) {
|
|
if(bits & 1) {
|
|
//EMUTRACE1("voiceoff %2d:",a);
|
|
voice_off(state->chan + a);
|
|
}
|
|
bits >>= 1;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** - Scales samples in a buffer using an envelope
|
|
** - Returns the actual number of samples modified
|
|
**
|
|
** All UNMODIFIED samples must then be discarded
|
|
** (they're supposed to be zero)
|
|
*/
|
|
static int EMU_CALL enveloper(
|
|
struct SPUCORE_ENVELOPE *env,
|
|
sint32 *buf,
|
|
int samples
|
|
) {
|
|
int i = 0;
|
|
while(i < samples) {
|
|
sint32 e, d, max;
|
|
if(env->state == ENVELOPE_STATE_OFF) break;
|
|
max = env->cachemax;
|
|
if(!max) {
|
|
max = envelope_do(env);
|
|
env->cachemax = max;
|
|
}
|
|
e = env->level;
|
|
d = env->delta;
|
|
if(max < 1) max = 1;
|
|
if(max > (samples - i)) { max = samples - i; }
|
|
env->cachemax -= max;
|
|
if(!buf) {
|
|
i += max;
|
|
e += max * d;
|
|
} else {
|
|
while(max--) {
|
|
sint32 b = buf[i];
|
|
b *= (e >> 16);
|
|
b >>= 15;
|
|
buf[i] = b;
|
|
e += d;
|
|
i++;
|
|
}
|
|
}
|
|
env->level = e;
|
|
env->delta = d;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// - Calls the resampler and enveloper
|
|
// - Returns the number of samples actually generated
|
|
// (can be 0 if the channel is silent)
|
|
//
|
|
// If buf is null, it means "fast forward" - don't render anything
|
|
//
|
|
//
|
|
// output of render_channel_raw() is guaranteed clipped as long as
|
|
// resampler() and enveloper() are also
|
|
//
|
|
static int EMU_CALL render_channel_raw(
|
|
uint16 *ram,
|
|
uint32 memmax,
|
|
struct SPUCORE_CHAN *c,
|
|
sint32 *buf,
|
|
sint32 *fmbuf,
|
|
sint32 *nbuf,
|
|
int samples,
|
|
struct SPUCORE_IRQ_STATE *irq_state
|
|
) {
|
|
int r = samples;
|
|
/* If the envelope is dead, don't bother anyway */
|
|
if((c->env.state) == ENVELOPE_STATE_OFF) return 0;
|
|
/* Do resampling */
|
|
if (fmbuf) r = resampler_modulated(ram, memmax, &(c->sample), buf, r, c->voice_pitch, fmbuf, irq_state);
|
|
else r = resampler(ram, memmax, &(c->sample), buf, r, c->voice_pitch, irq_state);
|
|
if(nbuf) {
|
|
r = samples;
|
|
if(buf) memcpy(buf, nbuf, 4 * r);
|
|
}
|
|
/* Do enveloping */
|
|
r = enveloper(&(c->env), buf, r);
|
|
/* If we were cut short by _either_, then the envelope state must be set
|
|
** to OFF */
|
|
if(r < samples) c->env.state = ENVELOPE_STATE_OFF;
|
|
return r;
|
|
}
|
|
|
|
//
|
|
// This is the new render_channel which processes deferred key-ons
|
|
//
|
|
//
|
|
// output of render_channel_mono() is guaranteed clipped as long as
|
|
// render_channel_raw() is also
|
|
//
|
|
static int EMU_CALL render_channel_mono(
|
|
uint16 *ram,
|
|
uint32 memmax,
|
|
struct SPUCORE_CHAN *c,
|
|
sint32 *buf,
|
|
sint32 *fmbuf,
|
|
sint32 *nbuf,
|
|
sint32 samples,
|
|
struct SPUCORE_IRQ_STATE *irq_state
|
|
) {
|
|
sint32 n;
|
|
sint32 r, r2;
|
|
sint32 defer_remaining;
|
|
struct SPUCORE_IRQ_STATE spare_state;
|
|
|
|
n = c->samples_until_keyoff_responds;
|
|
if (n > samples) { n = samples; }
|
|
c->samples_until_keyoff_responds -= n;
|
|
|
|
//top:
|
|
n = c->samples_until_pending_keyon;
|
|
|
|
if(!n) {
|
|
return render_channel_raw(ram, memmax, c, buf, fmbuf, nbuf, samples, irq_state);
|
|
}
|
|
|
|
//
|
|
// Don't defer key-on if fast-forwarding
|
|
// this caused a STUCK-NOTE BUG. should not use.
|
|
//
|
|
// if(!buf) {
|
|
// c->samples_until_pending_keyon = 0;
|
|
// goto top;
|
|
// }
|
|
|
|
if(n > samples) { n = samples; }
|
|
|
|
/*
|
|
** r = how many samples we actually will process
|
|
*/
|
|
r = render_channel_raw(ram, memmax, c, buf, fmbuf, nbuf, n, irq_state);
|
|
|
|
defer_remaining = c->samples_until_pending_keyon;
|
|
if(buf) {
|
|
sint32 i;
|
|
for(i = 0; i < r; i++) {
|
|
(*buf) = ((*buf)*defer_remaining) / KEYON_DEFER_SAMPLES;
|
|
buf++;
|
|
defer_remaining--;
|
|
}
|
|
} else {
|
|
defer_remaining -= r;
|
|
}
|
|
if(fmbuf) fmbuf += r;
|
|
if(nbuf) nbuf += r;
|
|
/*
|
|
** if render_channel_raw got cut short, then we're done anyway.
|
|
*/
|
|
if(r < n) defer_remaining = 0;
|
|
|
|
c->samples_until_pending_keyon = defer_remaining;
|
|
/*
|
|
** Process the key-on if necessary
|
|
*/
|
|
if(!defer_remaining) {
|
|
sample_prime(&(c->sample));
|
|
envelope_prime(&(c->env));
|
|
}
|
|
|
|
/*
|
|
** we already handled r samples
|
|
*/
|
|
samples -= r;
|
|
|
|
/*
|
|
** if there are any remaining samples to render, do them here
|
|
*/
|
|
r2 = 0;
|
|
if(samples) {
|
|
struct SPUCORE_IRQ_STATE *s = NULL;
|
|
if(irq_state) {
|
|
s = &spare_state;
|
|
spare_state.offset = irq_state->offset;
|
|
}
|
|
r2 = render_channel_raw(ram, memmax, c, buf, fmbuf, nbuf, samples, s);
|
|
if(irq_state && irq_state->triggered_cycle == 0xFFFFFFFF && spare_state.triggered_cycle != 0xFFFFFFFF) irq_state->triggered_cycle = spare_state.triggered_cycle + r * 768;
|
|
}
|
|
|
|
return r + r2;
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Generates a buffer worth of noise data, ganked from Eternal SPU
|
|
//
|
|
|
|
static void EMU_CALL render_noise(
|
|
struct SPUCORE_STATE *state,
|
|
sint32 *buf,
|
|
sint32 samples
|
|
) {
|
|
int n;
|
|
uint32 noisecounter = state->noisecounter;
|
|
sint32 noiseval = state->noiseval;
|
|
uint32 noiseinc = (uint16)(0x8000 >> (state->noiseclock << 6));
|
|
for(n = 0; n < samples; n++) {
|
|
noisecounter += noiseinc;
|
|
noiseval += noisecounter + noisecounter + noisetable[(noisecounter >> 10) & 63];
|
|
if (noiseval < -32767) noiseval = -32767;
|
|
else if (noiseval > 32767) noiseval = 32767;
|
|
if (buf) *buf++ = noiseval;
|
|
}
|
|
state->noisecounter = noisecounter;
|
|
state->noiseval = noiseval;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MAKE_SINT32_COEF(x) ((sint32)((sint16)(state->reverb.x)))
|
|
#define MAKE_REVERB_OFFSET(x) (state->reverb.x)
|
|
#define NORMALIZE_REVERB_OFFSET(x) {while(x>=state->reverb.safe_end_address){x-=state->reverb.safe_size;}while(x<state->reverb.safe_start_address){x+=state->reverb.safe_size;}}
|
|
|
|
#define RAM_PCM_SAMPLE(x) (*((sint16*)(((uint8*)ram) + (x))))
|
|
#define RAM_SINT32_SAMPLE(x) ((sint32)(RAM_PCM_SAMPLE(x)))
|
|
/*
|
|
** Multiplying -32768 by -32768 and scaling by 15 bits is not safe
|
|
** (the sign would get flipped)
|
|
** so we should clip to -32767 instead
|
|
*/
|
|
#define CLIP_PCM_1(x) {if(x>32767){x=32767;}else if(x<(-32767)){x=(-32767);}}
|
|
#define CLIP_PCM_2(a,b) {CLIP_PCM_1(a);CLIP_PCM_1(b);}
|
|
#define CLIP_PCM_4(a,b,c,d) {CLIP_PCM_1(a);CLIP_PCM_1(b);CLIP_PCM_1(c);CLIP_PCM_1(d);}
|
|
|
|
//#define CLIP_PCMDBL_1(x) {if(x>(32767*2)){x=(32767*2);}else if(x<(-(32767*2))){x=(-(32767*2));}}
|
|
//#define CLIP_PCMDBL_2(a,b) {CLIP_PCMDBL_1(a);CLIP_PCMDBL_1(b);}
|
|
//#define CLIP_PCMDBL_4(a,b,c,d) {CLIP_PCMDBL_1(a);CLIP_PCMDBL_1(b);CLIP_PCMDBL_1(c);CLIP_PCMDBL_1(d);}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** 22KHz reverb steady state step
|
|
*/
|
|
static void EMU_CALL reverb_steadystate22(struct SPUCORE_STATE *state, uint16 *ram, sint32 input_l, sint32 input_r) {
|
|
/*
|
|
** Current reverb offset
|
|
*/
|
|
sint32 current = state->reverb.current_address;
|
|
/*
|
|
** Reverb registers
|
|
*/
|
|
sint32 fb_src_a = MAKE_REVERB_OFFSET(FB_SRC_A);
|
|
sint32 fb_src_b = MAKE_REVERB_OFFSET(FB_SRC_B);
|
|
sint32 iir_alpha = MAKE_SINT32_COEF(IIR_ALPHA);
|
|
sint32 acc_coef_a = MAKE_SINT32_COEF(ACC_COEF_A);
|
|
sint32 acc_coef_b = MAKE_SINT32_COEF(ACC_COEF_B);
|
|
sint32 acc_coef_c = MAKE_SINT32_COEF(ACC_COEF_C);
|
|
sint32 acc_coef_d = MAKE_SINT32_COEF(ACC_COEF_D);
|
|
sint32 iir_coef = MAKE_SINT32_COEF(IIR_COEF);
|
|
sint32 fb_alpha = MAKE_SINT32_COEF(FB_ALPHA);
|
|
sint32 fb_x = MAKE_SINT32_COEF(FB_X);
|
|
sint32 iir_dest_a0 = MAKE_REVERB_OFFSET(IIR_DEST_A0);
|
|
sint32 iir_dest_a1 = MAKE_REVERB_OFFSET(IIR_DEST_A1);
|
|
sint32 acc_src_a0 = MAKE_REVERB_OFFSET(ACC_SRC_A0);
|
|
sint32 acc_src_a1 = MAKE_REVERB_OFFSET(ACC_SRC_A1);
|
|
sint32 acc_src_b0 = MAKE_REVERB_OFFSET(ACC_SRC_B0);
|
|
sint32 acc_src_b1 = MAKE_REVERB_OFFSET(ACC_SRC_B1);
|
|
sint32 iir_src_a0 = MAKE_REVERB_OFFSET(IIR_SRC_A0);
|
|
sint32 iir_src_a1 = MAKE_REVERB_OFFSET(IIR_SRC_A1);
|
|
sint32 iir_dest_b0 = MAKE_REVERB_OFFSET(IIR_DEST_B0);
|
|
sint32 iir_dest_b1 = MAKE_REVERB_OFFSET(IIR_DEST_B1);
|
|
sint32 acc_src_c0 = MAKE_REVERB_OFFSET(ACC_SRC_C0);
|
|
sint32 acc_src_c1 = MAKE_REVERB_OFFSET(ACC_SRC_C1);
|
|
sint32 acc_src_d0 = MAKE_REVERB_OFFSET(ACC_SRC_D0);
|
|
sint32 acc_src_d1 = MAKE_REVERB_OFFSET(ACC_SRC_D1);
|
|
sint32 iir_src_b1 = MAKE_REVERB_OFFSET(IIR_SRC_B1);
|
|
sint32 iir_src_b0 = MAKE_REVERB_OFFSET(IIR_SRC_B0);
|
|
sint32 mix_dest_a0 = MAKE_REVERB_OFFSET(MIX_DEST_A0);
|
|
sint32 mix_dest_a1 = MAKE_REVERB_OFFSET(MIX_DEST_A1);
|
|
sint32 mix_dest_b0 = MAKE_REVERB_OFFSET(MIX_DEST_B0);
|
|
sint32 mix_dest_b1 = MAKE_REVERB_OFFSET(MIX_DEST_B1);
|
|
sint32 in_coef_l = MAKE_SINT32_COEF(IN_COEF_L);
|
|
sint32 in_coef_r = MAKE_SINT32_COEF(IN_COEF_R);
|
|
/*
|
|
** Alternate buffer positions
|
|
*/
|
|
sint32 fb_src_a0;
|
|
sint32 fb_src_a1;
|
|
sint32 fb_src_b0;
|
|
sint32 fb_src_b1;
|
|
sint32 iir_dest_a0_plus;
|
|
sint32 iir_dest_a1_plus;
|
|
sint32 iir_dest_b0_plus;
|
|
sint32 iir_dest_b1_plus;
|
|
/*
|
|
** Intermediate results
|
|
*/
|
|
sint32 acc0;
|
|
sint32 acc1;
|
|
sint32 iir_input_a0;
|
|
sint32 iir_input_a1;
|
|
sint32 iir_input_b0;
|
|
sint32 iir_input_b1;
|
|
sint32 iir_a0;
|
|
sint32 iir_a1;
|
|
sint32 iir_b0;
|
|
sint32 iir_b1;
|
|
sint32 fb_a0;
|
|
sint32 fb_a1;
|
|
sint32 fb_b0;
|
|
sint32 fb_b1;
|
|
sint32 mix_a0;
|
|
sint32 mix_a1;
|
|
sint32 mix_b0;
|
|
sint32 mix_b1;
|
|
|
|
/*
|
|
** Offsets
|
|
*/
|
|
mix_dest_a0 += current; NORMALIZE_REVERB_OFFSET(mix_dest_a0);
|
|
mix_dest_a1 += current; NORMALIZE_REVERB_OFFSET(mix_dest_a1);
|
|
mix_dest_b0 += current; NORMALIZE_REVERB_OFFSET(mix_dest_b0);
|
|
mix_dest_b1 += current; NORMALIZE_REVERB_OFFSET(mix_dest_b1);
|
|
fb_src_a0 = mix_dest_a0 - fb_src_a; NORMALIZE_REVERB_OFFSET(fb_src_a0);
|
|
fb_src_a1 = mix_dest_a1 - fb_src_a; NORMALIZE_REVERB_OFFSET(fb_src_a1);
|
|
fb_src_b0 = mix_dest_b0 - fb_src_b; NORMALIZE_REVERB_OFFSET(fb_src_b0);
|
|
fb_src_b1 = mix_dest_b1 - fb_src_b; NORMALIZE_REVERB_OFFSET(fb_src_b1);
|
|
acc_src_a0 += current; NORMALIZE_REVERB_OFFSET(acc_src_a0);
|
|
acc_src_a1 += current; NORMALIZE_REVERB_OFFSET(acc_src_a1);
|
|
acc_src_b0 += current; NORMALIZE_REVERB_OFFSET(acc_src_b0);
|
|
acc_src_b1 += current; NORMALIZE_REVERB_OFFSET(acc_src_b1);
|
|
acc_src_c0 += current; NORMALIZE_REVERB_OFFSET(acc_src_c0);
|
|
acc_src_c1 += current; NORMALIZE_REVERB_OFFSET(acc_src_c1);
|
|
acc_src_d0 += current; NORMALIZE_REVERB_OFFSET(acc_src_d0);
|
|
acc_src_d1 += current; NORMALIZE_REVERB_OFFSET(acc_src_d1);
|
|
iir_src_a0 += current; NORMALIZE_REVERB_OFFSET(iir_src_a0);
|
|
iir_src_a1 += current; NORMALIZE_REVERB_OFFSET(iir_src_a1);
|
|
iir_src_b0 += current; NORMALIZE_REVERB_OFFSET(iir_src_b0);
|
|
iir_src_b1 += current; NORMALIZE_REVERB_OFFSET(iir_src_b1);
|
|
iir_dest_a0 += current; NORMALIZE_REVERB_OFFSET(iir_dest_a0);
|
|
iir_dest_a1 += current; NORMALIZE_REVERB_OFFSET(iir_dest_a1);
|
|
iir_dest_b0 += current; NORMALIZE_REVERB_OFFSET(iir_dest_b0);
|
|
iir_dest_b1 += current; NORMALIZE_REVERB_OFFSET(iir_dest_b1);
|
|
iir_dest_a0_plus = iir_dest_a0 + 2; NORMALIZE_REVERB_OFFSET(iir_dest_a0_plus);
|
|
iir_dest_a1_plus = iir_dest_a1 + 2; NORMALIZE_REVERB_OFFSET(iir_dest_a1_plus);
|
|
iir_dest_b0_plus = iir_dest_b0 + 2; NORMALIZE_REVERB_OFFSET(iir_dest_b0_plus);
|
|
iir_dest_b1_plus = iir_dest_b1 + 2; NORMALIZE_REVERB_OFFSET(iir_dest_b1_plus);
|
|
|
|
/*
|
|
** IIR
|
|
*/
|
|
CLIP_PCM_2(input_l,input_r);
|
|
input_l *= in_coef_l;
|
|
input_r *= in_coef_r;
|
|
#define OPPOSITE_IIR_ALPHA (32768-iir_alpha)
|
|
iir_input_a0 = ((RAM_SINT32_SAMPLE(iir_src_a0) * iir_coef) + input_l) >> 15;
|
|
iir_input_a1 = ((RAM_SINT32_SAMPLE(iir_src_a1) * iir_coef) + input_r) >> 15;
|
|
iir_input_b0 = ((RAM_SINT32_SAMPLE(iir_src_b0) * iir_coef) + input_l) >> 15;
|
|
iir_input_b1 = ((RAM_SINT32_SAMPLE(iir_src_b1) * iir_coef) + input_r) >> 15;
|
|
CLIP_PCM_4(iir_input_a0,iir_input_a1,iir_input_b0,iir_input_b1);
|
|
iir_a0 = ((iir_input_a0 * iir_alpha) + (RAM_SINT32_SAMPLE(iir_dest_a0) * (OPPOSITE_IIR_ALPHA))) >> 15;
|
|
iir_a1 = ((iir_input_a1 * iir_alpha) + (RAM_SINT32_SAMPLE(iir_dest_a1) * (OPPOSITE_IIR_ALPHA))) >> 15;
|
|
iir_b0 = ((iir_input_b0 * iir_alpha) + (RAM_SINT32_SAMPLE(iir_dest_b0) * (OPPOSITE_IIR_ALPHA))) >> 15;
|
|
iir_b1 = ((iir_input_b1 * iir_alpha) + (RAM_SINT32_SAMPLE(iir_dest_b1) * (OPPOSITE_IIR_ALPHA))) >> 15;
|
|
CLIP_PCM_4(iir_a0,iir_a1,iir_b0,iir_b1);
|
|
|
|
RAM_PCM_SAMPLE(iir_dest_a0_plus) = iir_a0;
|
|
RAM_PCM_SAMPLE(iir_dest_a1_plus) = iir_a1;
|
|
RAM_PCM_SAMPLE(iir_dest_b0_plus) = iir_b0;
|
|
RAM_PCM_SAMPLE(iir_dest_b1_plus) = iir_b1;
|
|
|
|
/*
|
|
** Accumulators
|
|
*/
|
|
acc0 =
|
|
((RAM_SINT32_SAMPLE(acc_src_a0) * acc_coef_a) >> 15) +
|
|
((RAM_SINT32_SAMPLE(acc_src_b0) * acc_coef_b) >> 15) +
|
|
((RAM_SINT32_SAMPLE(acc_src_c0) * acc_coef_c) >> 15) +
|
|
((RAM_SINT32_SAMPLE(acc_src_d0) * acc_coef_d) >> 15);
|
|
acc1 =
|
|
((RAM_SINT32_SAMPLE(acc_src_a1) * acc_coef_a) >> 15) +
|
|
((RAM_SINT32_SAMPLE(acc_src_b1) * acc_coef_b) >> 15) +
|
|
((RAM_SINT32_SAMPLE(acc_src_c1) * acc_coef_c) >> 15) +
|
|
((RAM_SINT32_SAMPLE(acc_src_d1) * acc_coef_d) >> 15);
|
|
CLIP_PCM_2(acc0,acc1);
|
|
|
|
/*
|
|
** Feedback
|
|
*/
|
|
fb_a0 = RAM_SINT32_SAMPLE(fb_src_a0);
|
|
fb_a1 = RAM_SINT32_SAMPLE(fb_src_a1);
|
|
fb_b0 = RAM_SINT32_SAMPLE(fb_src_b0);
|
|
fb_b1 = RAM_SINT32_SAMPLE(fb_src_b1);
|
|
|
|
mix_a0 = acc0 - ((fb_a0*fb_alpha)>>15);
|
|
mix_a1 = acc1 - ((fb_a1*fb_alpha)>>15);
|
|
mix_b0 = fb_alpha*acc0;
|
|
mix_b1 = fb_alpha*acc1;
|
|
fb_alpha = ((sint32)((sint16)(((sint16)fb_alpha)^0x8000)));
|
|
mix_b0 -= fb_a0*fb_alpha;
|
|
mix_b1 -= fb_a1*fb_alpha;
|
|
mix_b0 -= fb_b0*fb_x;
|
|
mix_b1 -= fb_b1*fb_x;
|
|
mix_b0>>=15;
|
|
mix_b1>>=15;
|
|
|
|
CLIP_PCM_4(mix_a0,mix_a1,mix_b0,mix_b1);
|
|
RAM_PCM_SAMPLE(mix_dest_a0) = mix_a0;
|
|
RAM_PCM_SAMPLE(mix_dest_a1) = mix_a1;
|
|
RAM_PCM_SAMPLE(mix_dest_b0) = mix_b0;
|
|
RAM_PCM_SAMPLE(mix_dest_b1) = mix_b1;
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** 22KHz reverb engine
|
|
*/
|
|
static void EMU_CALL reverb_engine22(struct SPUCORE_STATE *state, uint16 *ram, sint32 *l, sint32 *r) {
|
|
sint32 input_l = *l;
|
|
sint32 input_r = *r;
|
|
sint32 output_l;
|
|
sint32 output_r;
|
|
sint32 mix_dest_a0 = state->reverb.current_address + MAKE_REVERB_OFFSET(MIX_DEST_A0);
|
|
sint32 mix_dest_a1 = state->reverb.current_address + MAKE_REVERB_OFFSET(MIX_DEST_A1);
|
|
sint32 mix_dest_b0 = state->reverb.current_address + MAKE_REVERB_OFFSET(MIX_DEST_B0);
|
|
sint32 mix_dest_b1 = state->reverb.current_address + MAKE_REVERB_OFFSET(MIX_DEST_B1);
|
|
NORMALIZE_REVERB_OFFSET(mix_dest_a0);
|
|
NORMALIZE_REVERB_OFFSET(mix_dest_a1);
|
|
NORMALIZE_REVERB_OFFSET(mix_dest_b0);
|
|
NORMALIZE_REVERB_OFFSET(mix_dest_b1);
|
|
|
|
/*
|
|
** (Scale these down for now - avoids some clipping)
|
|
*/
|
|
input_l *= 2;
|
|
input_r *= 2;
|
|
input_l /= 3;
|
|
input_r /= 3;
|
|
|
|
/*
|
|
** Execute steady state step if necessary
|
|
*/
|
|
if(state->flags & SPUREG_FLAG_REVERB_ENABLE) {
|
|
reverb_steadystate22(state, ram, input_l, input_r);
|
|
}
|
|
|
|
/*
|
|
** Retrieve wet out L/R
|
|
** (pretty certain this is done AFTER the steady state step)
|
|
*/
|
|
{
|
|
int al = RAM_SINT32_SAMPLE(mix_dest_a0);
|
|
int ar = RAM_SINT32_SAMPLE(mix_dest_a1);
|
|
int bl = RAM_SINT32_SAMPLE(mix_dest_b0);
|
|
int br = RAM_SINT32_SAMPLE(mix_dest_b1);
|
|
|
|
output_l = al + bl;
|
|
output_r = ar + br;
|
|
}
|
|
|
|
*l = output_l;
|
|
*r = output_r;
|
|
|
|
/*
|
|
** Advance reverb buffer position
|
|
*/
|
|
state->reverb.current_address += 2;
|
|
if(state->reverb.current_address >= state->reverb.safe_end_address) {
|
|
state->reverb.current_address = state->reverb.safe_start_address;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void EMU_CALL reverb_process(struct SPUCORE_STATE *state, uint16 *ram, sint32 *buf, int samples) {
|
|
int q = state->reverb.resampler.queue_index;
|
|
/*
|
|
** Sample loop
|
|
*/
|
|
while(samples--) {
|
|
/*
|
|
** Get an input sample
|
|
*/
|
|
sint32 l = buf[0];
|
|
sint32 r = buf[1];
|
|
/*
|
|
** Put it in the input queue
|
|
*/
|
|
state->reverb.resampler.in_queue_l[q & 63] = l;
|
|
state->reverb.resampler.in_queue_r[q & 63] = r;
|
|
/*
|
|
** If we're ready to create another output sample...
|
|
*/
|
|
if(q & 1) {
|
|
/*
|
|
** Lowpass/downsample
|
|
*/
|
|
#if 1
|
|
l =
|
|
(state->reverb.resampler.in_queue_l[(q - 38) & 63]) * reverb_psx_lowpass_coefs[0] +
|
|
(state->reverb.resampler.in_queue_l[(q - 36) & 63]) * reverb_psx_lowpass_coefs[1] +
|
|
(state->reverb.resampler.in_queue_l[(q - 34) & 63]) * reverb_psx_lowpass_coefs[2] +
|
|
(state->reverb.resampler.in_queue_l[(q - 32) & 63]) * reverb_psx_lowpass_coefs[3] +
|
|
(state->reverb.resampler.in_queue_l[(q - 30) & 63]) * reverb_psx_lowpass_coefs[4] +
|
|
(state->reverb.resampler.in_queue_l[(q - 28) & 63]) * reverb_psx_lowpass_coefs[5] +
|
|
(state->reverb.resampler.in_queue_l[(q - 26) & 63]) * reverb_psx_lowpass_coefs[6] +
|
|
(state->reverb.resampler.in_queue_l[(q - 24) & 63]) * reverb_psx_lowpass_coefs[7] +
|
|
(state->reverb.resampler.in_queue_l[(q - 22) & 63]) * reverb_psx_lowpass_coefs[8] +
|
|
(state->reverb.resampler.in_queue_l[(q - 20) & 63]) * reverb_psx_lowpass_coefs[9] +
|
|
(state->reverb.resampler.in_queue_l[(q - 19) & 63]) * reverb_psx_lowpass_coefs[10] +
|
|
(state->reverb.resampler.in_queue_l[(q - 18) & 63]) * reverb_psx_lowpass_coefs[9] +
|
|
(state->reverb.resampler.in_queue_l[(q - 16) & 63]) * reverb_psx_lowpass_coefs[8] +
|
|
(state->reverb.resampler.in_queue_l[(q - 14) & 63]) * reverb_psx_lowpass_coefs[7] +
|
|
(state->reverb.resampler.in_queue_l[(q - 12) & 63]) * reverb_psx_lowpass_coefs[6] +
|
|
(state->reverb.resampler.in_queue_l[(q - 10) & 63]) * reverb_psx_lowpass_coefs[5] +
|
|
(state->reverb.resampler.in_queue_l[(q - 8) & 63]) * reverb_psx_lowpass_coefs[4] +
|
|
(state->reverb.resampler.in_queue_l[(q - 6) & 63]) * reverb_psx_lowpass_coefs[3] +
|
|
(state->reverb.resampler.in_queue_l[(q - 4) & 63]) * reverb_psx_lowpass_coefs[2] +
|
|
(state->reverb.resampler.in_queue_l[(q - 2) & 63]) * reverb_psx_lowpass_coefs[1] +
|
|
(state->reverb.resampler.in_queue_l[(q - 0) & 63]) * reverb_psx_lowpass_coefs[0];
|
|
r =
|
|
(state->reverb.resampler.in_queue_r[(q - 38) & 63]) * reverb_psx_lowpass_coefs[0] +
|
|
(state->reverb.resampler.in_queue_r[(q - 36) & 63]) * reverb_psx_lowpass_coefs[1] +
|
|
(state->reverb.resampler.in_queue_r[(q - 34) & 63]) * reverb_psx_lowpass_coefs[2] +
|
|
(state->reverb.resampler.in_queue_r[(q - 32) & 63]) * reverb_psx_lowpass_coefs[3] +
|
|
(state->reverb.resampler.in_queue_r[(q - 30) & 63]) * reverb_psx_lowpass_coefs[4] +
|
|
(state->reverb.resampler.in_queue_r[(q - 28) & 63]) * reverb_psx_lowpass_coefs[5] +
|
|
(state->reverb.resampler.in_queue_r[(q - 26) & 63]) * reverb_psx_lowpass_coefs[6] +
|
|
(state->reverb.resampler.in_queue_r[(q - 24) & 63]) * reverb_psx_lowpass_coefs[7] +
|
|
(state->reverb.resampler.in_queue_r[(q - 22) & 63]) * reverb_psx_lowpass_coefs[8] +
|
|
(state->reverb.resampler.in_queue_r[(q - 20) & 63]) * reverb_psx_lowpass_coefs[9] +
|
|
(state->reverb.resampler.in_queue_r[(q - 19) & 63]) * reverb_psx_lowpass_coefs[10] +
|
|
(state->reverb.resampler.in_queue_r[(q - 18) & 63]) * reverb_psx_lowpass_coefs[9] +
|
|
(state->reverb.resampler.in_queue_r[(q - 16) & 63]) * reverb_psx_lowpass_coefs[8] +
|
|
(state->reverb.resampler.in_queue_r[(q - 14) & 63]) * reverb_psx_lowpass_coefs[7] +
|
|
(state->reverb.resampler.in_queue_r[(q - 12) & 63]) * reverb_psx_lowpass_coefs[6] +
|
|
(state->reverb.resampler.in_queue_r[(q - 10) & 63]) * reverb_psx_lowpass_coefs[5] +
|
|
(state->reverb.resampler.in_queue_r[(q - 8) & 63]) * reverb_psx_lowpass_coefs[4] +
|
|
(state->reverb.resampler.in_queue_r[(q - 6) & 63]) * reverb_psx_lowpass_coefs[3] +
|
|
(state->reverb.resampler.in_queue_r[(q - 4) & 63]) * reverb_psx_lowpass_coefs[2] +
|
|
(state->reverb.resampler.in_queue_r[(q - 2) & 63]) * reverb_psx_lowpass_coefs[1] +
|
|
(state->reverb.resampler.in_queue_r[(q - 0) & 63]) * reverb_psx_lowpass_coefs[0];
|
|
#else
|
|
l = 0;
|
|
r = 0;
|
|
|
|
for (n = 47; n >= 0; n -= 8) {
|
|
l +=
|
|
(state->reverb.resampler.in_queue_l[(q - n + 0) & 63]) * reverb_psx_lowpass_coefs[n - 0] +
|
|
(state->reverb.resampler.in_queue_l[(q - n + 1) & 63]) * reverb_psx_lowpass_coefs[n - 1] +
|
|
(state->reverb.resampler.in_queue_l[(q - n + 2) & 63]) * reverb_psx_lowpass_coefs[n - 2] +
|
|
(state->reverb.resampler.in_queue_l[(q - n + 3) & 63]) * reverb_psx_lowpass_coefs[n - 3] +
|
|
(state->reverb.resampler.in_queue_l[(q - n + 4) & 63]) * reverb_psx_lowpass_coefs[n - 4] +
|
|
(state->reverb.resampler.in_queue_l[(q - n + 5) & 63]) * reverb_psx_lowpass_coefs[n - 5] +
|
|
(state->reverb.resampler.in_queue_l[(q - n + 6) & 63]) * reverb_psx_lowpass_coefs[n - 6] +
|
|
(state->reverb.resampler.in_queue_l[(q - n + 7) & 63]) * reverb_psx_lowpass_coefs[n - 7];
|
|
r +=
|
|
(state->reverb.resampler.in_queue_r[(q - n + 0) & 63]) * reverb_psx_lowpass_coefs[n - 0] +
|
|
(state->reverb.resampler.in_queue_r[(q - n + 1) & 63]) * reverb_psx_lowpass_coefs[n - 1] +
|
|
(state->reverb.resampler.in_queue_r[(q - n + 2) & 63]) * reverb_psx_lowpass_coefs[n - 2] +
|
|
(state->reverb.resampler.in_queue_r[(q - n + 3) & 63]) * reverb_psx_lowpass_coefs[n - 3] +
|
|
(state->reverb.resampler.in_queue_r[(q - n + 4) & 63]) * reverb_psx_lowpass_coefs[n - 4] +
|
|
(state->reverb.resampler.in_queue_r[(q - n + 5) & 63]) * reverb_psx_lowpass_coefs[n - 5] +
|
|
(state->reverb.resampler.in_queue_r[(q - n + 6) & 63]) * reverb_psx_lowpass_coefs[n - 6] +
|
|
(state->reverb.resampler.in_queue_r[(q - n + 7) & 63]) * reverb_psx_lowpass_coefs[n - 7];
|
|
}
|
|
#endif
|
|
|
|
l >>= 15;
|
|
r >>= 15;
|
|
|
|
/*
|
|
l =
|
|
(state->reverb.resampler.in_queue_l[(q - 6) & 7]) * reverb_new_lowpass_coefs[0] +
|
|
(state->reverb.resampler.in_queue_l[(q - 4) & 7]) * reverb_new_lowpass_coefs[1] +
|
|
(state->reverb.resampler.in_queue_l[(q - 3) & 7]) * reverb_new_lowpass_coefs[2] +
|
|
(state->reverb.resampler.in_queue_l[(q - 2) & 7]) * reverb_new_lowpass_coefs[1] +
|
|
(state->reverb.resampler.in_queue_l[(q - 0) & 7]) * reverb_new_lowpass_coefs[0];
|
|
l >>= 11;
|
|
r =
|
|
(state->reverb.resampler.in_queue_r[(q - 6) & 7]) * reverb_new_lowpass_coefs[0] +
|
|
(state->reverb.resampler.in_queue_r[(q - 4) & 7]) * reverb_new_lowpass_coefs[1] +
|
|
(state->reverb.resampler.in_queue_r[(q - 3) & 7]) * reverb_new_lowpass_coefs[2] +
|
|
(state->reverb.resampler.in_queue_r[(q - 2) & 7]) * reverb_new_lowpass_coefs[1] +
|
|
(state->reverb.resampler.in_queue_r[(q - 0) & 7]) * reverb_new_lowpass_coefs[0];
|
|
r >>= 11;
|
|
*/
|
|
//l = state->reverb.resampler.in_queue_l[q & 7];
|
|
//r = state->reverb.resampler.in_queue_r[q & 7];
|
|
|
|
/*
|
|
** Run the reverb engine
|
|
*/
|
|
reverb_engine22(state, ram, &l, &r);
|
|
/*
|
|
** Put the new stuff into the output queue
|
|
*/
|
|
state->reverb.resampler.out_queue_l[q & 15] = l;
|
|
state->reverb.resampler.out_queue_r[q & 15] = r;
|
|
}
|
|
/*
|
|
** Upsample
|
|
*/
|
|
/*
|
|
** Upsample using the same technique as for ADPCM samples
|
|
** (may or may not be right, sounds okay though)
|
|
*/
|
|
#define gauss_table_0x000 gauss_shuffled_reverse_table[0x200]
|
|
#define gauss_table_0x100 gauss_shuffled_reverse_table[0x201]
|
|
#define gauss_table_0x200 gauss_shuffled_reverse_table[0x202]
|
|
#define gauss_table_0x300 gauss_shuffled_reverse_table[0x203]
|
|
#define gauss_table_0x080 gauss_shuffled_reverse_table[0x000]
|
|
#define gauss_table_0x180 gauss_shuffled_reverse_table[0x001]
|
|
#define gauss_table_0x280 gauss_shuffled_reverse_table[0x002]
|
|
#define gauss_table_0x380 gauss_shuffled_reverse_table[0x003]
|
|
if(q & 1) {
|
|
l =
|
|
(state->reverb.resampler.out_queue_l[(q - 6) & 15]) * gauss_table_0x080 +
|
|
(state->reverb.resampler.out_queue_l[(q - 4) & 15]) * gauss_table_0x180 +
|
|
(state->reverb.resampler.out_queue_l[(q - 2) & 15]) * gauss_table_0x280 +
|
|
(state->reverb.resampler.out_queue_l[(q - 0) & 15]) * gauss_table_0x380;
|
|
l >>= 15;
|
|
r =
|
|
(state->reverb.resampler.out_queue_r[(q - 6) & 15]) * gauss_table_0x080 +
|
|
(state->reverb.resampler.out_queue_r[(q - 4) & 15]) * gauss_table_0x180 +
|
|
(state->reverb.resampler.out_queue_r[(q - 2) & 15]) * gauss_table_0x280 +
|
|
(state->reverb.resampler.out_queue_r[(q - 0) & 15]) * gauss_table_0x380;
|
|
r >>= 15;
|
|
} else {
|
|
l =
|
|
(state->reverb.resampler.out_queue_l[(q - 7) & 15]) * gauss_table_0x000 +
|
|
(state->reverb.resampler.out_queue_l[(q - 5) & 15]) * gauss_table_0x100 +
|
|
(state->reverb.resampler.out_queue_l[(q - 3) & 15]) * gauss_table_0x200 +
|
|
(state->reverb.resampler.out_queue_l[(q - 1) & 15]) * gauss_table_0x300;
|
|
l >>= 15;
|
|
r =
|
|
(state->reverb.resampler.out_queue_r[(q - 7) & 15]) * gauss_table_0x000 +
|
|
(state->reverb.resampler.out_queue_r[(q - 5) & 15]) * gauss_table_0x100 +
|
|
(state->reverb.resampler.out_queue_r[(q - 3) & 15]) * gauss_table_0x200 +
|
|
(state->reverb.resampler.out_queue_r[(q - 1) & 15]) * gauss_table_0x300;
|
|
r >>= 15;
|
|
}
|
|
/*
|
|
** Advance queue position, write output, all that good stuff
|
|
*/
|
|
q++;
|
|
buf[0] = l;
|
|
buf[1] = r;
|
|
buf += 2;
|
|
}
|
|
state->reverb.resampler.queue_index = q;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//int spucore_frq[RENDERMAX];
|
|
|
|
/*
|
|
** Renderer
|
|
*/
|
|
static void EMU_CALL render(struct SPUCORE_STATE *state, uint16 *ram, sint16 *buf, sint16 *extinput, sint32 samples, uint8 mainout, uint8 effectout) {
|
|
uint32 chanbit;
|
|
uint32 maskmain_l;
|
|
uint32 maskmain_r;
|
|
uint32 maskverb_l;
|
|
uint32 maskverb_r;
|
|
uint32 masknoise;
|
|
uint32 maskfm;
|
|
sint32 ibuf [ RENDERMAX];
|
|
sint32 ibufmix[2*RENDERMAX];
|
|
sint32 ibufrvb[2*RENDERMAX];
|
|
sint32 ibufn [ RENDERMAX];
|
|
sint32 ibuffm [ RENDERMAX];
|
|
int ch, i;
|
|
sint32 m_v_l;
|
|
sint32 m_v_r;
|
|
sint32 r_v_l;
|
|
sint32 r_v_r;
|
|
struct SPUCORE_IRQ_STATE irq_state;
|
|
struct SPUCORE_IRQ_STATE *irq_state_ptr;
|
|
|
|
//spucore_frq[samples]++;
|
|
|
|
irq_state_ptr = NULL;
|
|
irq_state.triggered_cycle = 0xFFFFFFFF;
|
|
if (state->flags & SPUREG_FLAG_IRQ_ENABLE) {
|
|
if (state->memsize == 0x80000 && state->irq_address < 0x1000) {
|
|
uint32 irq_address_masked = state->irq_address & 0x3FF;
|
|
uint32 irq_sample_offset = irq_address_masked - state->irq_decoder_clock;
|
|
if(irq_sample_offset > 0x3FF) irq_sample_offset += 0x400;
|
|
if (irq_sample_offset < (uint32)samples) {
|
|
irq_state.triggered_cycle = irq_sample_offset * 768;
|
|
}
|
|
} else {
|
|
irq_state_ptr = &irq_state;
|
|
irq_state.offset = state->irq_address;
|
|
}
|
|
}
|
|
|
|
state->irq_decoder_clock = (state->irq_decoder_clock + samples) & 0x3FF;
|
|
|
|
memset(ibufmix, 0, 8 * samples);
|
|
if(effectout) {
|
|
memset(ibufrvb, 0, 8 * samples);
|
|
}
|
|
|
|
maskmain_l = 0;
|
|
maskmain_r = 0;
|
|
maskverb_l = 0;
|
|
maskverb_r = 0;
|
|
if(state->flags & SPUREG_FLAG_MSNDL) maskmain_l = state->vmix[0] & 0xFFFFFF;
|
|
if(state->flags & SPUREG_FLAG_MSNDR) maskmain_r = state->vmix[1] & 0xFFFFFF;
|
|
if(effectout) {
|
|
if(state->flags & SPUREG_FLAG_MSNDEL) maskverb_l = state->vmixe[0] & 0xFFFFFF;
|
|
if(state->flags & SPUREG_FLAG_MSNDER) maskverb_r = state->vmixe[1] & 0xFFFFFF;
|
|
}
|
|
masknoise = state->noise;
|
|
render_noise(state, masknoise ? ibufn : NULL, samples);
|
|
maskfm = state->fm & 0xFFFFFE;
|
|
|
|
if(!mainout) { maskmain_l = 0; maskmain_r = 0; }
|
|
|
|
for(ch = 0, chanbit = 1; ch < 24; ch++, chanbit <<= 1) {
|
|
int r;
|
|
sint32 v_l, v_r;
|
|
uint32 main_l = chanbit & maskmain_l;
|
|
uint32 main_r = chanbit & maskmain_r;
|
|
uint32 verb_l = chanbit & maskverb_l;
|
|
uint32 verb_r = chanbit & maskverb_r;
|
|
sint32 *b = buf ? ibuf : NULL;
|
|
sint32 *fm = (chanbit & maskfm) ? ibuffm : NULL;
|
|
sint32 *noise = (chanbit & masknoise) ? ibufn : NULL;
|
|
if(!(main_l | main_r | verb_l | verb_r)) b = NULL;
|
|
r = render_channel_mono(
|
|
ram, state->memsize, state->chan + ch, b, fm, noise, samples, irq_state_ptr
|
|
);
|
|
if(!b) {
|
|
memset(ibuffm, 0, 4 * samples);
|
|
continue;
|
|
}
|
|
memcpy(ibuffm, ibuf, 4 * r);
|
|
if(r < samples) memset(ibuffm + r, 0, 4 * (samples-r));
|
|
v_l = volume_getlevel(state->chan[ch].vol+0);
|
|
v_r = volume_getlevel(state->chan[ch].vol+1);
|
|
for(i = 0; i < r; i++) {
|
|
sint32 q_l = (v_l * ibuf[i]) >> 16;
|
|
sint32 q_r = (v_r * ibuf[i]) >> 16;
|
|
if(main_l) ibufmix[2*i+0] += q_l;
|
|
if(main_r) ibufmix[2*i+1] += q_r;
|
|
if(verb_l) ibufrvb[2*i+0] += q_l;
|
|
if(verb_r) ibufrvb[2*i+1] += q_r;
|
|
}
|
|
}
|
|
|
|
state->irq_triggered_cycle = irq_state.triggered_cycle;
|
|
|
|
if(!buf) return;
|
|
|
|
/*
|
|
** Mix in external input, if it exists
|
|
*/
|
|
if(extinput) {
|
|
sint32 extvol_l = ((sint16)(state->avol[0]));
|
|
sint32 extvol_r = ((sint16)(state->avol[1]));
|
|
if(extvol_l == -0x8000) extvol_l = -0x7FFF;
|
|
if(extvol_r == -0x8000) extvol_r = -0x7FFF;
|
|
for(i = 0; i < samples; i++) {
|
|
sint32 sin_l = extinput[2*i+0];
|
|
sint32 sin_r = extinput[2*i+1];
|
|
sin_l *= extvol_l;
|
|
sin_r *= extvol_r;
|
|
sin_l >>= 15;
|
|
sin_r >>= 15;
|
|
if(state->flags & SPUREG_FLAG_SINL ) ibufmix[2*i+0] += sin_l;
|
|
if(state->flags & SPUREG_FLAG_SINR ) ibufmix[2*i+1] += sin_r;
|
|
if(state->flags & SPUREG_FLAG_SINEL) ibufrvb[2*i+0] += sin_l;
|
|
if(state->flags & SPUREG_FLAG_SINER) ibufrvb[2*i+1] += sin_r;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Do reverb
|
|
** This handles both writing into the buffer and retrieving
|
|
** values out of it, resampling to/from 22KHz, etc.
|
|
*/
|
|
if(effectout) {
|
|
reverb_process(state, ram, ibufrvb, samples);
|
|
}
|
|
/*
|
|
**
|
|
*/
|
|
m_v_l = volume_getlevel(state->mvol+0);
|
|
m_v_r = volume_getlevel(state->mvol+1);
|
|
r_v_l = ((sint16)(state->evol[0]));
|
|
r_v_r = ((sint16)(state->evol[1]));
|
|
|
|
if(!effectout) {
|
|
for(i = 0; i < samples; i++) {
|
|
sint64 q_l = ibufmix[2*i+0];
|
|
sint64 q_r = ibufmix[2*i+1];
|
|
q_l *= (sint64)m_v_l;
|
|
q_r *= (sint64)m_v_r;
|
|
q_l >>= 15;
|
|
q_r >>= 15;
|
|
CLIP_PCM_2(q_l,q_r);
|
|
*buf++ = (sint16)q_l;
|
|
*buf++ = (sint16)q_r;
|
|
}
|
|
} else {
|
|
for(i = 0; i < samples; i++) {
|
|
sint64 q_l = ibufmix[2*i+0];
|
|
sint64 q_r = ibufmix[2*i+1];
|
|
sint64 r_l = ibufrvb[2*i+0];
|
|
sint64 r_r = ibufrvb[2*i+1];
|
|
q_l *= (sint64)m_v_l;
|
|
q_r *= (sint64)m_v_r;
|
|
r_l *= (sint64)r_v_l;
|
|
r_r *= (sint64)r_v_r;
|
|
q_l >>= 15;
|
|
q_r >>= 15;
|
|
r_l >>= 15;
|
|
r_r >>= 15;
|
|
q_l += r_l;
|
|
q_r += r_r;
|
|
CLIP_PCM_2(q_l, q_r);
|
|
*buf++ = (sint16)q_l;
|
|
*buf++ = (sint16)q_r;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** Externally-accessible renderer
|
|
*/
|
|
void EMU_CALL spucore_render(void *state, uint16 *ram, sint16 *buf, sint16 *extinput, uint32 samples, uint8 mainout, uint8 effectout) {
|
|
while(samples > RENDERMAX) {
|
|
samples -= RENDERMAX;
|
|
render(SPUCORESTATE, ram, buf, extinput, RENDERMAX, mainout, effectout);
|
|
if(buf ) buf += 2 * RENDERMAX;
|
|
if(extinput) extinput += 2 * RENDERMAX;
|
|
}
|
|
if(samples) render(SPUCORESTATE, ram, buf, extinput, samples, mainout, effectout);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** Flag get/set
|
|
*/
|
|
|
|
int EMU_CALL spucore_getflag(void *state, uint32 n) {
|
|
return !!(SPUCORESTATE->flags & n);
|
|
}
|
|
|
|
void EMU_CALL spucore_setflag(void *state, uint32 n, int value) {
|
|
if(value) {
|
|
SPUCORESTATE->flags |= n;
|
|
} else {
|
|
SPUCORESTATE->flags &= ~n;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** Register get/set
|
|
*/
|
|
|
|
uint32 EMU_CALL spucore_getreg(void *state, uint32 n) {
|
|
switch(n) {
|
|
case SPUREG_MVOLL: return volume_getmode (SPUCORESTATE->mvol+0) & 0x0000FFFF;
|
|
case SPUREG_MVOLR: return volume_getmode (SPUCORESTATE->mvol+1) & 0x0000FFFF;
|
|
case SPUREG_MVOLXL: return volume_getlevel(SPUCORESTATE->mvol+0) & 0x0000FFFF;
|
|
case SPUREG_MVOLXR: return volume_getlevel(SPUCORESTATE->mvol+1) & 0x0000FFFF;
|
|
case SPUREG_EVOLL: return SPUCORESTATE->evol[0] & 0x0000FFFF;
|
|
case SPUREG_EVOLR: return SPUCORESTATE->evol[1] & 0x0000FFFF;
|
|
case SPUREG_AVOLL: return SPUCORESTATE->avol[0] & 0x0000FFFF;
|
|
case SPUREG_AVOLR: return SPUCORESTATE->avol[1] & 0x0000FFFF;
|
|
case SPUREG_BVOLL: return SPUCORESTATE->bvol[0] & 0x0000FFFF;
|
|
case SPUREG_BVOLR: return SPUCORESTATE->bvol[1] & 0x0000FFFF;
|
|
case SPUREG_KON: return SPUCORESTATE->kon & 0x00FFFFFF;
|
|
case SPUREG_KOFF: return SPUCORESTATE->koff & 0x00FFFFFF;
|
|
case SPUREG_FM: return SPUCORESTATE->fm & 0x00FFFFFF;
|
|
case SPUREG_NOISE: return SPUCORESTATE->noise & 0x00FFFFFF;
|
|
case SPUREG_VMIXE: return (SPUCORESTATE->vmixe[0] | SPUCORESTATE->vmixe[1]) & 0xFFFFFF;
|
|
case SPUREG_VMIX: return (SPUCORESTATE->vmix[0] | SPUCORESTATE->vmix[1] ) & 0xFFFFFF;
|
|
case SPUREG_VMIXEL: return SPUCORESTATE->vmixe[0] & 0x00FFFFFF;
|
|
case SPUREG_VMIXER: return SPUCORESTATE->vmixe[1] & 0x00FFFFFF;
|
|
case SPUREG_VMIXL: return SPUCORESTATE->vmix[0] & 0x00FFFFFF;
|
|
case SPUREG_VMIXR: return SPUCORESTATE->vmix[1] & 0x00FFFFFF;
|
|
case SPUREG_ESA: return SPUCORESTATE->reverb.start_address;
|
|
case SPUREG_EEA: return SPUCORESTATE->reverb.end_address;
|
|
case SPUREG_EAX: return SPUCORESTATE->reverb.current_address;
|
|
case SPUREG_IRQA: return SPUCORESTATE->irq_address;
|
|
case SPUREG_NOISECLOCK: return SPUCORESTATE->noiseclock;
|
|
#define SPUCORE_REVERB_GET(x) case SPUREG_REVERB_##x:return SPUCORESTATE->reverb.x;
|
|
SPUCORE_REVERB_GET(FB_SRC_A)
|
|
SPUCORE_REVERB_GET(FB_SRC_B)
|
|
SPUCORE_REVERB_GET(IIR_ALPHA)
|
|
SPUCORE_REVERB_GET(ACC_COEF_A)
|
|
SPUCORE_REVERB_GET(ACC_COEF_B)
|
|
SPUCORE_REVERB_GET(ACC_COEF_C)
|
|
SPUCORE_REVERB_GET(ACC_COEF_D)
|
|
SPUCORE_REVERB_GET(IIR_COEF)
|
|
SPUCORE_REVERB_GET(FB_ALPHA)
|
|
SPUCORE_REVERB_GET(FB_X)
|
|
SPUCORE_REVERB_GET(IIR_DEST_A0)
|
|
SPUCORE_REVERB_GET(IIR_DEST_A1)
|
|
SPUCORE_REVERB_GET(ACC_SRC_A0)
|
|
SPUCORE_REVERB_GET(ACC_SRC_A1)
|
|
SPUCORE_REVERB_GET(ACC_SRC_B0)
|
|
SPUCORE_REVERB_GET(ACC_SRC_B1)
|
|
SPUCORE_REVERB_GET(IIR_SRC_A0)
|
|
SPUCORE_REVERB_GET(IIR_SRC_A1)
|
|
SPUCORE_REVERB_GET(IIR_DEST_B0)
|
|
SPUCORE_REVERB_GET(IIR_DEST_B1)
|
|
SPUCORE_REVERB_GET(ACC_SRC_C0)
|
|
SPUCORE_REVERB_GET(ACC_SRC_C1)
|
|
SPUCORE_REVERB_GET(ACC_SRC_D0)
|
|
SPUCORE_REVERB_GET(ACC_SRC_D1)
|
|
SPUCORE_REVERB_GET(IIR_SRC_B1)
|
|
SPUCORE_REVERB_GET(IIR_SRC_B0)
|
|
SPUCORE_REVERB_GET(MIX_DEST_A0)
|
|
SPUCORE_REVERB_GET(MIX_DEST_A1)
|
|
SPUCORE_REVERB_GET(MIX_DEST_B0)
|
|
SPUCORE_REVERB_GET(MIX_DEST_B1)
|
|
SPUCORE_REVERB_GET(IN_COEF_L)
|
|
SPUCORE_REVERB_GET(IN_COEF_R)
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void EMU_CALL spucore_setreg(void *state, uint32 n, uint32 value, uint32 mask) {
|
|
value &= mask;
|
|
switch(n) {
|
|
/* TODO: the increase/decrease modes, etc. */
|
|
case SPUREG_MVOLL: volume_setmode(SPUCORESTATE->mvol+0, value); break;
|
|
case SPUREG_MVOLR: volume_setmode(SPUCORESTATE->mvol+1, value); break;
|
|
case SPUREG_EVOLL: SPUCORESTATE->evol[0] = value; break;
|
|
case SPUREG_EVOLR: SPUCORESTATE->evol[1] = value; break;
|
|
case SPUREG_AVOLL: SPUCORESTATE->avol[0] = value; break;
|
|
case SPUREG_AVOLR: SPUCORESTATE->avol[1] = value; break;
|
|
case SPUREG_BVOLL: SPUCORESTATE->bvol[0] = value; break;
|
|
case SPUREG_BVOLR: SPUCORESTATE->bvol[1] = value; break;
|
|
|
|
case SPUREG_KON:
|
|
SPUCORESTATE->kon &= ~mask;
|
|
SPUCORESTATE->kon |= value;
|
|
voices_on(state, value);
|
|
break;
|
|
case SPUREG_KOFF:
|
|
SPUCORESTATE->koff &= ~mask;
|
|
SPUCORESTATE->koff |= value;
|
|
voices_off(state, value);
|
|
break;
|
|
|
|
case SPUREG_FM:
|
|
SPUCORESTATE->fm &= ~mask;
|
|
SPUCORESTATE->fm |= value;
|
|
break;
|
|
case SPUREG_NOISE:
|
|
SPUCORESTATE->noise &= ~mask;
|
|
SPUCORESTATE->noise |= value;
|
|
break;
|
|
case SPUREG_VMIXE:
|
|
SPUCORESTATE->vmixe[0] &= ~mask;
|
|
SPUCORESTATE->vmixe[0] |= value;
|
|
SPUCORESTATE->vmixe[1] &= ~mask;
|
|
SPUCORESTATE->vmixe[1] |= value;
|
|
break;
|
|
case SPUREG_VMIX:
|
|
SPUCORESTATE->vmix[0] &= ~mask;
|
|
SPUCORESTATE->vmix[0] |= value;
|
|
SPUCORESTATE->vmix[1] &= ~mask;
|
|
SPUCORESTATE->vmix[1] |= value;
|
|
break;
|
|
case SPUREG_VMIXEL:
|
|
SPUCORESTATE->vmixe[0] &= ~mask;
|
|
SPUCORESTATE->vmixe[0] |= value;
|
|
break;
|
|
case SPUREG_VMIXER:
|
|
SPUCORESTATE->vmixe[1] &= ~mask;
|
|
SPUCORESTATE->vmixe[1] |= value;
|
|
break;
|
|
case SPUREG_VMIXL:
|
|
SPUCORESTATE->vmix[0] &= ~mask;
|
|
SPUCORESTATE->vmix[0] |= value;
|
|
break;
|
|
case SPUREG_VMIXR:
|
|
SPUCORESTATE->vmix[1] &= ~mask;
|
|
SPUCORESTATE->vmix[1] |= value;
|
|
break;
|
|
|
|
case SPUREG_ESA:
|
|
SPUCORESTATE->reverb.start_address &= ~mask;
|
|
SPUCORESTATE->reverb.start_address |= value;
|
|
make_safe_reverb_addresses(state);
|
|
SPUCORESTATE->reverb.current_address = SPUCORESTATE->reverb.safe_start_address;
|
|
break;
|
|
case SPUREG_EEA:
|
|
SPUCORESTATE->reverb.end_address &= ~mask;
|
|
SPUCORESTATE->reverb.end_address |= value;
|
|
make_safe_reverb_addresses(state);
|
|
break;
|
|
case SPUREG_IRQA:
|
|
/* TODO: actual implementation of IRQs */
|
|
SPUCORESTATE->irq_address &= ~mask;
|
|
SPUCORESTATE->irq_address |= value;
|
|
break;
|
|
case SPUREG_NOISECLOCK:
|
|
SPUCORESTATE->noiseclock = value & 0x3F;
|
|
break;
|
|
|
|
#define SPUCORE_REVERB_SET(x) case SPUREG_REVERB_##x:SPUCORESTATE->reverb.x&=(~mask);SPUCORESTATE->reverb.x|=value;break;
|
|
SPUCORE_REVERB_SET(FB_SRC_A)
|
|
SPUCORE_REVERB_SET(FB_SRC_B)
|
|
SPUCORE_REVERB_SET(IIR_ALPHA)
|
|
SPUCORE_REVERB_SET(ACC_COEF_A)
|
|
SPUCORE_REVERB_SET(ACC_COEF_B)
|
|
SPUCORE_REVERB_SET(ACC_COEF_C)
|
|
SPUCORE_REVERB_SET(ACC_COEF_D)
|
|
SPUCORE_REVERB_SET(IIR_COEF)
|
|
SPUCORE_REVERB_SET(FB_ALPHA)
|
|
SPUCORE_REVERB_SET(FB_X)
|
|
SPUCORE_REVERB_SET(IIR_DEST_A0)
|
|
SPUCORE_REVERB_SET(IIR_DEST_A1)
|
|
SPUCORE_REVERB_SET(ACC_SRC_A0)
|
|
SPUCORE_REVERB_SET(ACC_SRC_A1)
|
|
SPUCORE_REVERB_SET(ACC_SRC_B0)
|
|
SPUCORE_REVERB_SET(ACC_SRC_B1)
|
|
SPUCORE_REVERB_SET(IIR_SRC_A0)
|
|
SPUCORE_REVERB_SET(IIR_SRC_A1)
|
|
SPUCORE_REVERB_SET(IIR_DEST_B0)
|
|
SPUCORE_REVERB_SET(IIR_DEST_B1)
|
|
SPUCORE_REVERB_SET(ACC_SRC_C0)
|
|
SPUCORE_REVERB_SET(ACC_SRC_C1)
|
|
SPUCORE_REVERB_SET(ACC_SRC_D0)
|
|
SPUCORE_REVERB_SET(ACC_SRC_D1)
|
|
SPUCORE_REVERB_SET(IIR_SRC_B1)
|
|
SPUCORE_REVERB_SET(IIR_SRC_B0)
|
|
SPUCORE_REVERB_SET(MIX_DEST_A0)
|
|
SPUCORE_REVERB_SET(MIX_DEST_A1)
|
|
SPUCORE_REVERB_SET(MIX_DEST_B0)
|
|
SPUCORE_REVERB_SET(MIX_DEST_B1)
|
|
SPUCORE_REVERB_SET(IN_COEF_L)
|
|
SPUCORE_REVERB_SET(IN_COEF_R)
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** Voice register get/set
|
|
*/
|
|
|
|
uint32 EMU_CALL spucore_getreg_voice(void *state, uint32 voice, uint32 n) {
|
|
switch(n) {
|
|
case SPUREG_VOICE_VOLL : return volume_getmode(SPUCORESTATE->chan[voice].vol+0);
|
|
case SPUREG_VOICE_VOLR : return volume_getmode(SPUCORESTATE->chan[voice].vol+1);
|
|
case SPUREG_VOICE_VOLXL: return volume_getlevel(SPUCORESTATE->chan[voice].vol+0);
|
|
case SPUREG_VOICE_VOLXR: return volume_getlevel(SPUCORESTATE->chan[voice].vol+1);
|
|
case SPUREG_VOICE_PITCH: return SPUCORESTATE->chan[voice].voice_pitch;
|
|
case SPUREG_VOICE_ADSR1: return SPUCORESTATE->chan[voice].env.reg_ad;
|
|
case SPUREG_VOICE_ADSR2: return SPUCORESTATE->chan[voice].env.reg_sr;
|
|
case SPUREG_VOICE_ENVX :
|
|
if(SPUCORESTATE->chan[voice].env.state == ENVELOPE_STATE_OFF) return 0;
|
|
return (SPUCORESTATE->chan[voice].env.level) >> 16;
|
|
case SPUREG_VOICE_SSA : return SPUCORESTATE->chan[voice].sample.start_block_addr;
|
|
case SPUREG_VOICE_LSAX : return SPUCORESTATE->chan[voice].sample.loop_block_addr;
|
|
case SPUREG_VOICE_NAX : return SPUCORESTATE->chan[voice].sample.block_addr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void EMU_CALL spucore_setreg_voice(void *state, uint32 voice, uint32 n, uint32 value, uint32 mask) {
|
|
value &= mask;
|
|
switch(n) {
|
|
/* TODO: the increase/decrease modes */
|
|
case SPUREG_VOICE_VOLL : volume_setmode(SPUCORESTATE->chan[voice].vol+0, value); break;
|
|
case SPUREG_VOICE_VOLR : volume_setmode(SPUCORESTATE->chan[voice].vol+1, value); break;
|
|
case SPUREG_VOICE_PITCH: SPUCORESTATE->chan[voice].voice_pitch = value; break;
|
|
case SPUREG_VOICE_ADSR1: SPUCORESTATE->chan[voice].env.reg_ad = value; SPUCORESTATE->chan[voice].env.cachemax = envelope_do(&SPUCORESTATE->chan[voice].env); break;
|
|
case SPUREG_VOICE_ADSR2: SPUCORESTATE->chan[voice].env.reg_sr = value; SPUCORESTATE->chan[voice].env.cachemax = envelope_do(&SPUCORESTATE->chan[voice].env); break;
|
|
|
|
case SPUREG_VOICE_SSA:
|
|
SPUCORESTATE->chan[voice].sample.start_block_addr &= ~mask;
|
|
SPUCORESTATE->chan[voice].sample.start_block_addr |= value;
|
|
break;
|
|
case SPUREG_VOICE_LSAX:
|
|
SPUCORESTATE->chan[voice].sample.loop_block_addr &= ~mask;
|
|
SPUCORESTATE->chan[voice].sample.loop_block_addr |= value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
** IRQ checking
|
|
*/
|
|
uint32 EMU_CALL spucore_cycles_until_interrupt(void *state, uint16 *ram, uint32 samples) {
|
|
uint32 r;
|
|
void *backup;
|
|
|
|
if (!(SPUCORESTATE->flags & SPUREG_FLAG_IRQ_ENABLE)) return 0xFFFFFFFF;
|
|
|
|
backup = malloc(spucore_get_state_size());
|
|
if (!backup) return 0xFFFFFFFF;
|
|
memcpy(backup, state, spucore_get_state_size());
|
|
state = backup;
|
|
SPUCORESTATE->irq_triggered_cycle = 0xFFFFFFFF;
|
|
r = 0;
|
|
while(samples > RENDERMAX) {
|
|
samples -= RENDERMAX;
|
|
render(SPUCORESTATE, ram, NULL, NULL, RENDERMAX, 0, 0);
|
|
if (SPUCORESTATE->irq_triggered_cycle != 0xFFFFFFFF) break;
|
|
r += RENDERMAX * 768;
|
|
}
|
|
if(samples && SPUCORESTATE->irq_triggered_cycle == 0xFFFFFFFF) render(SPUCORESTATE, ram, NULL, NULL, samples, 0, 0);
|
|
r = (SPUCORESTATE->irq_triggered_cycle == 0xFFFFFFFF) ? 0xFFFFFFFF : SPUCORESTATE->irq_triggered_cycle + r;
|
|
free(backup);
|
|
return r;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|