looper/backends/playback/zsm/x16emu/ymfm/src/ymfm_fm.ipp
2024-09-06 10:06:32 -07:00

1590 lines
58 KiB
C++

// BSD 3-Clause License
//
// Copyright (c) 2021, Aaron Giles
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace ymfm
{
//*********************************************************
// GLOBAL TABLE LOOKUPS
//*********************************************************
//-------------------------------------------------
// abs_sin_attenuation - given a sin (phase) input
// where the range 0-2*PI is mapped onto 10 bits,
// return the absolute value of sin(input),
// logarithmically-adjusted and treated as an
// attenuation value, in 4.8 fixed point format
//-------------------------------------------------
inline uint32_t abs_sin_attenuation(uint32_t input)
{
// the values here are stored as 4.8 logarithmic values for 1/4 phase
// this matches the internal format of the OPN chip, extracted from the die
static uint16_t const s_sin_table[256] =
{
0x859,0x6c3,0x607,0x58b,0x52e,0x4e4,0x4a6,0x471,0x443,0x41a,0x3f5,0x3d3,0x3b5,0x398,0x37e,0x365,
0x34e,0x339,0x324,0x311,0x2ff,0x2ed,0x2dc,0x2cd,0x2bd,0x2af,0x2a0,0x293,0x286,0x279,0x26d,0x261,
0x256,0x24b,0x240,0x236,0x22c,0x222,0x218,0x20f,0x206,0x1fd,0x1f5,0x1ec,0x1e4,0x1dc,0x1d4,0x1cd,
0x1c5,0x1be,0x1b7,0x1b0,0x1a9,0x1a2,0x19b,0x195,0x18f,0x188,0x182,0x17c,0x177,0x171,0x16b,0x166,
0x160,0x15b,0x155,0x150,0x14b,0x146,0x141,0x13c,0x137,0x133,0x12e,0x129,0x125,0x121,0x11c,0x118,
0x114,0x10f,0x10b,0x107,0x103,0x0ff,0x0fb,0x0f8,0x0f4,0x0f0,0x0ec,0x0e9,0x0e5,0x0e2,0x0de,0x0db,
0x0d7,0x0d4,0x0d1,0x0cd,0x0ca,0x0c7,0x0c4,0x0c1,0x0be,0x0bb,0x0b8,0x0b5,0x0b2,0x0af,0x0ac,0x0a9,
0x0a7,0x0a4,0x0a1,0x09f,0x09c,0x099,0x097,0x094,0x092,0x08f,0x08d,0x08a,0x088,0x086,0x083,0x081,
0x07f,0x07d,0x07a,0x078,0x076,0x074,0x072,0x070,0x06e,0x06c,0x06a,0x068,0x066,0x064,0x062,0x060,
0x05e,0x05c,0x05b,0x059,0x057,0x055,0x053,0x052,0x050,0x04e,0x04d,0x04b,0x04a,0x048,0x046,0x045,
0x043,0x042,0x040,0x03f,0x03e,0x03c,0x03b,0x039,0x038,0x037,0x035,0x034,0x033,0x031,0x030,0x02f,
0x02e,0x02d,0x02b,0x02a,0x029,0x028,0x027,0x026,0x025,0x024,0x023,0x022,0x021,0x020,0x01f,0x01e,
0x01d,0x01c,0x01b,0x01a,0x019,0x018,0x017,0x017,0x016,0x015,0x014,0x014,0x013,0x012,0x011,0x011,
0x010,0x00f,0x00f,0x00e,0x00d,0x00d,0x00c,0x00c,0x00b,0x00a,0x00a,0x009,0x009,0x008,0x008,0x007,
0x007,0x007,0x006,0x006,0x005,0x005,0x005,0x004,0x004,0x004,0x003,0x003,0x003,0x002,0x002,0x002,
0x002,0x001,0x001,0x001,0x001,0x001,0x001,0x001,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000
};
// if the top bit is set, we're in the second half of the curve
// which is a mirror image, so invert the index
if (bitfield(input, 8))
input = ~input;
// return the value from the table
return s_sin_table[input & 0xff];
}
//-------------------------------------------------
// attenuation_to_volume - given a 5.8 fixed point
// logarithmic attenuation value, return a 13-bit
// linear volume
//-------------------------------------------------
inline uint32_t attenuation_to_volume(uint32_t input)
{
// the values here are 10-bit mantissas with an implied leading bit
// this matches the internal format of the OPN chip, extracted from the die
// as a nod to performance, the implicit 0x400 bit is pre-incorporated, and
// the values are left-shifted by 2 so that a simple right shift is all that
// is needed; also the order is reversed to save a NOT on the input
#define X(a) (((a) | 0x400) << 2)
static uint16_t const s_power_table[256] =
{
X(0x3fa),X(0x3f5),X(0x3ef),X(0x3ea),X(0x3e4),X(0x3df),X(0x3da),X(0x3d4),
X(0x3cf),X(0x3c9),X(0x3c4),X(0x3bf),X(0x3b9),X(0x3b4),X(0x3ae),X(0x3a9),
X(0x3a4),X(0x39f),X(0x399),X(0x394),X(0x38f),X(0x38a),X(0x384),X(0x37f),
X(0x37a),X(0x375),X(0x370),X(0x36a),X(0x365),X(0x360),X(0x35b),X(0x356),
X(0x351),X(0x34c),X(0x347),X(0x342),X(0x33d),X(0x338),X(0x333),X(0x32e),
X(0x329),X(0x324),X(0x31f),X(0x31a),X(0x315),X(0x310),X(0x30b),X(0x306),
X(0x302),X(0x2fd),X(0x2f8),X(0x2f3),X(0x2ee),X(0x2e9),X(0x2e5),X(0x2e0),
X(0x2db),X(0x2d6),X(0x2d2),X(0x2cd),X(0x2c8),X(0x2c4),X(0x2bf),X(0x2ba),
X(0x2b5),X(0x2b1),X(0x2ac),X(0x2a8),X(0x2a3),X(0x29e),X(0x29a),X(0x295),
X(0x291),X(0x28c),X(0x288),X(0x283),X(0x27f),X(0x27a),X(0x276),X(0x271),
X(0x26d),X(0x268),X(0x264),X(0x25f),X(0x25b),X(0x257),X(0x252),X(0x24e),
X(0x249),X(0x245),X(0x241),X(0x23c),X(0x238),X(0x234),X(0x230),X(0x22b),
X(0x227),X(0x223),X(0x21e),X(0x21a),X(0x216),X(0x212),X(0x20e),X(0x209),
X(0x205),X(0x201),X(0x1fd),X(0x1f9),X(0x1f5),X(0x1f0),X(0x1ec),X(0x1e8),
X(0x1e4),X(0x1e0),X(0x1dc),X(0x1d8),X(0x1d4),X(0x1d0),X(0x1cc),X(0x1c8),
X(0x1c4),X(0x1c0),X(0x1bc),X(0x1b8),X(0x1b4),X(0x1b0),X(0x1ac),X(0x1a8),
X(0x1a4),X(0x1a0),X(0x19c),X(0x199),X(0x195),X(0x191),X(0x18d),X(0x189),
X(0x185),X(0x181),X(0x17e),X(0x17a),X(0x176),X(0x172),X(0x16f),X(0x16b),
X(0x167),X(0x163),X(0x160),X(0x15c),X(0x158),X(0x154),X(0x151),X(0x14d),
X(0x149),X(0x146),X(0x142),X(0x13e),X(0x13b),X(0x137),X(0x134),X(0x130),
X(0x12c),X(0x129),X(0x125),X(0x122),X(0x11e),X(0x11b),X(0x117),X(0x114),
X(0x110),X(0x10c),X(0x109),X(0x106),X(0x102),X(0x0ff),X(0x0fb),X(0x0f8),
X(0x0f4),X(0x0f1),X(0x0ed),X(0x0ea),X(0x0e7),X(0x0e3),X(0x0e0),X(0x0dc),
X(0x0d9),X(0x0d6),X(0x0d2),X(0x0cf),X(0x0cc),X(0x0c8),X(0x0c5),X(0x0c2),
X(0x0be),X(0x0bb),X(0x0b8),X(0x0b5),X(0x0b1),X(0x0ae),X(0x0ab),X(0x0a8),
X(0x0a4),X(0x0a1),X(0x09e),X(0x09b),X(0x098),X(0x094),X(0x091),X(0x08e),
X(0x08b),X(0x088),X(0x085),X(0x082),X(0x07e),X(0x07b),X(0x078),X(0x075),
X(0x072),X(0x06f),X(0x06c),X(0x069),X(0x066),X(0x063),X(0x060),X(0x05d),
X(0x05a),X(0x057),X(0x054),X(0x051),X(0x04e),X(0x04b),X(0x048),X(0x045),
X(0x042),X(0x03f),X(0x03c),X(0x039),X(0x036),X(0x033),X(0x030),X(0x02d),
X(0x02a),X(0x028),X(0x025),X(0x022),X(0x01f),X(0x01c),X(0x019),X(0x016),
X(0x014),X(0x011),X(0x00e),X(0x00b),X(0x008),X(0x006),X(0x003),X(0x000)
};
#undef X
// look up the fractional part, then shift by the whole
return s_power_table[input & 0xff] >> (input >> 8);
}
//-------------------------------------------------
// attenuation_increment - given a 6-bit ADSR
// rate value and a 3-bit stepping index,
// return a 4-bit increment to the attenutaion
// for this step (or for the attack case, the
// fractional scale factor to decrease by)
//-------------------------------------------------
inline uint32_t attenuation_increment(uint32_t rate, uint32_t index)
{
static uint32_t const s_increment_table[64] =
{
0x00000000, 0x00000000, 0x10101010, 0x10101010, // 0-3 (0x00-0x03)
0x10101010, 0x10101010, 0x11101110, 0x11101110, // 4-7 (0x04-0x07)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 8-11 (0x08-0x0B)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 12-15 (0x0C-0x0F)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 16-19 (0x10-0x13)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 20-23 (0x14-0x17)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 24-27 (0x18-0x1B)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 28-31 (0x1C-0x1F)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 32-35 (0x20-0x23)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 36-39 (0x24-0x27)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 40-43 (0x28-0x2B)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 44-47 (0x2C-0x2F)
0x11111111, 0x21112111, 0x21212121, 0x22212221, // 48-51 (0x30-0x33)
0x22222222, 0x42224222, 0x42424242, 0x44424442, // 52-55 (0x34-0x37)
0x44444444, 0x84448444, 0x84848484, 0x88848884, // 56-59 (0x38-0x3B)
0x88888888, 0x88888888, 0x88888888, 0x88888888 // 60-63 (0x3C-0x3F)
};
return bitfield(s_increment_table[rate], 4*index, 4);
}
//-------------------------------------------------
// detune_adjustment - given a 5-bit key code
// value and a 3-bit detune parameter, return a
// 6-bit signed phase displacement; this table
// has been verified against Nuked's equations,
// but the equations are rather complicated, so
// we'll keep the simplicity of the table
//-------------------------------------------------
inline int32_t detune_adjustment(uint32_t detune, uint32_t keycode)
{
static uint8_t const s_detune_adjustment[32][4] =
{
{ 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 },
{ 0, 1, 2, 2 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 },
{ 0, 1, 2, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 5 },
{ 0, 2, 4, 5 }, { 0, 2, 4, 6 }, { 0, 2, 4, 6 }, { 0, 2, 5, 7 },
{ 0, 2, 5, 8 }, { 0, 3, 6, 8 }, { 0, 3, 6, 9 }, { 0, 3, 7, 10 },
{ 0, 4, 8, 11 }, { 0, 4, 8, 12 }, { 0, 4, 9, 13 }, { 0, 5, 10, 14 },
{ 0, 5, 11, 16 }, { 0, 6, 12, 17 }, { 0, 6, 13, 19 }, { 0, 7, 14, 20 },
{ 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 }
};
int32_t result = s_detune_adjustment[keycode][detune & 3];
return bitfield(detune, 2) ? -result : result;
}
//-------------------------------------------------
// opm_key_code_to_phase_step - converts an
// OPM concatenated block (3 bits), keycode
// (4 bits) and key fraction (6 bits) to a 0.10
// phase step, after applying the given delta;
// this applies to OPM and OPZ, so it lives here
// in a central location
//-------------------------------------------------
inline uint32_t opm_key_code_to_phase_step(uint32_t block_freq, int32_t delta)
{
// The phase step is essentially the fnum in OPN-speak. To compute this table,
// we used the standard formula for computing the frequency of a note, and
// then converted that frequency to fnum using the formula documented in the
// YM2608 manual.
//
// However, the YM2608 manual describes everything in terms of a nominal 8MHz
// clock, which produces an FM clock of:
//
// 8000000 / 24(operators) / 6(prescale) = 55555Hz FM clock
//
// Whereas the descriptions for the YM2151 use a nominal 3.579545MHz clock:
//
// 3579545 / 32(operators) / 2(prescale) = 55930Hz FM clock
//
// To correct for this, the YM2608 formula was adjusted to use a clock of
// 8053920Hz, giving this equation for the fnum:
//
// fnum = (double(144) * freq * (1 << 20)) / double(8053920) / 4;
//
// Unfortunately, the computed table differs in a few spots from the data
// verified from an actual chip. The table below comes from David Viens'
// analysis, used with his permission.
static const uint32_t s_phase_step[12*64] =
{
41568,41600,41632,41664,41696,41728,41760,41792,41856,41888,41920,41952,42016,42048,42080,42112,
42176,42208,42240,42272,42304,42336,42368,42400,42464,42496,42528,42560,42624,42656,42688,42720,
42784,42816,42848,42880,42912,42944,42976,43008,43072,43104,43136,43168,43232,43264,43296,43328,
43392,43424,43456,43488,43552,43584,43616,43648,43712,43744,43776,43808,43872,43904,43936,43968,
44032,44064,44096,44128,44192,44224,44256,44288,44352,44384,44416,44448,44512,44544,44576,44608,
44672,44704,44736,44768,44832,44864,44896,44928,44992,45024,45056,45088,45152,45184,45216,45248,
45312,45344,45376,45408,45472,45504,45536,45568,45632,45664,45728,45760,45792,45824,45888,45920,
45984,46016,46048,46080,46144,46176,46208,46240,46304,46336,46368,46400,46464,46496,46528,46560,
46656,46688,46720,46752,46816,46848,46880,46912,46976,47008,47072,47104,47136,47168,47232,47264,
47328,47360,47392,47424,47488,47520,47552,47584,47648,47680,47744,47776,47808,47840,47904,47936,
48032,48064,48096,48128,48192,48224,48288,48320,48384,48416,48448,48480,48544,48576,48640,48672,
48736,48768,48800,48832,48896,48928,48992,49024,49088,49120,49152,49184,49248,49280,49344,49376,
49440,49472,49504,49536,49600,49632,49696,49728,49792,49824,49856,49888,49952,49984,50048,50080,
50144,50176,50208,50240,50304,50336,50400,50432,50496,50528,50560,50592,50656,50688,50752,50784,
50880,50912,50944,50976,51040,51072,51136,51168,51232,51264,51328,51360,51424,51456,51488,51520,
51616,51648,51680,51712,51776,51808,51872,51904,51968,52000,52064,52096,52160,52192,52224,52256,
52384,52416,52448,52480,52544,52576,52640,52672,52736,52768,52832,52864,52928,52960,52992,53024,
53120,53152,53216,53248,53312,53344,53408,53440,53504,53536,53600,53632,53696,53728,53792,53824,
53920,53952,54016,54048,54112,54144,54208,54240,54304,54336,54400,54432,54496,54528,54592,54624,
54688,54720,54784,54816,54880,54912,54976,55008,55072,55104,55168,55200,55264,55296,55360,55392,
55488,55520,55584,55616,55680,55712,55776,55808,55872,55936,55968,56032,56064,56128,56160,56224,
56288,56320,56384,56416,56480,56512,56576,56608,56672,56736,56768,56832,56864,56928,56960,57024,
57120,57152,57216,57248,57312,57376,57408,57472,57536,57568,57632,57664,57728,57792,57824,57888,
57952,57984,58048,58080,58144,58208,58240,58304,58368,58400,58464,58496,58560,58624,58656,58720,
58784,58816,58880,58912,58976,59040,59072,59136,59200,59232,59296,59328,59392,59456,59488,59552,
59648,59680,59744,59776,59840,59904,59936,60000,60064,60128,60160,60224,60288,60320,60384,60416,
60512,60544,60608,60640,60704,60768,60800,60864,60928,60992,61024,61088,61152,61184,61248,61280,
61376,61408,61472,61536,61600,61632,61696,61760,61824,61856,61920,61984,62048,62080,62144,62208,
62272,62304,62368,62432,62496,62528,62592,62656,62720,62752,62816,62880,62944,62976,63040,63104,
63200,63232,63296,63360,63424,63456,63520,63584,63648,63680,63744,63808,63872,63904,63968,64032,
64096,64128,64192,64256,64320,64352,64416,64480,64544,64608,64672,64704,64768,64832,64896,64928,
65024,65056,65120,65184,65248,65312,65376,65408,65504,65536,65600,65664,65728,65792,65856,65888,
65984,66016,66080,66144,66208,66272,66336,66368,66464,66496,66560,66624,66688,66752,66816,66848,
66944,66976,67040,67104,67168,67232,67296,67328,67424,67456,67520,67584,67648,67712,67776,67808,
67904,67936,68000,68064,68128,68192,68256,68288,68384,68448,68512,68544,68640,68672,68736,68800,
68896,68928,68992,69056,69120,69184,69248,69280,69376,69440,69504,69536,69632,69664,69728,69792,
69920,69952,70016,70080,70144,70208,70272,70304,70400,70464,70528,70560,70656,70688,70752,70816,
70912,70976,71040,71104,71136,71232,71264,71360,71424,71488,71552,71616,71648,71744,71776,71872,
71968,72032,72096,72160,72192,72288,72320,72416,72480,72544,72608,72672,72704,72800,72832,72928,
72992,73056,73120,73184,73216,73312,73344,73440,73504,73568,73632,73696,73728,73824,73856,73952,
74080,74144,74208,74272,74304,74400,74432,74528,74592,74656,74720,74784,74816,74912,74944,75040,
75136,75200,75264,75328,75360,75456,75488,75584,75648,75712,75776,75840,75872,75968,76000,76096,
76224,76288,76352,76416,76448,76544,76576,76672,76736,76800,76864,76928,77024,77120,77152,77248,
77344,77408,77472,77536,77568,77664,77696,77792,77856,77920,77984,78048,78144,78240,78272,78368,
78464,78528,78592,78656,78688,78784,78816,78912,78976,79040,79104,79168,79264,79360,79392,79488,
79616,79680,79744,79808,79840,79936,79968,80064,80128,80192,80256,80320,80416,80512,80544,80640,
80768,80832,80896,80960,80992,81088,81120,81216,81280,81344,81408,81472,81568,81664,81696,81792,
81952,82016,82080,82144,82176,82272,82304,82400,82464,82528,82592,82656,82752,82848,82880,82976
};
// extract the block (octave) first
uint32_t block = bitfield(block_freq, 10, 3);
// the keycode (bits 6-9) is "gappy", mapping 12 values over 16 in each
// octave; to correct for this, we multiply the 4-bit value by 3/4 (or
// rather subtract 1/4); note that a (invalid) value of 15 will bleed into
// the next octave -- this is confirmed
uint32_t adjusted_code = bitfield(block_freq, 6, 4) - bitfield(block_freq, 8, 2);
// now re-insert the 6-bit fraction
int32_t eff_freq = (adjusted_code << 6) | bitfield(block_freq, 0, 6);
// now that the gaps are removed, add the delta
eff_freq += delta;
// handle over/underflow by adjusting the block:
if (uint32_t(eff_freq) >= 768)
{
// minimum delta is -512 (PM), so we can only underflow by 1 octave
if (eff_freq < 0)
{
eff_freq += 768;
if (block-- == 0)
return s_phase_step[0] >> 7;
}
// maximum delta is +512+608 (PM+detune), so we can overflow by up to 2 octaves
else
{
eff_freq -= 768;
if (eff_freq >= 768)
block++, eff_freq -= 768;
if (block++ >= 7)
return s_phase_step[767];
}
}
// look up the phase shift for the key code, then shift by octave
return s_phase_step[eff_freq] >> (block ^ 7);
}
//-------------------------------------------------
// opn_lfo_pm_phase_adjustment - given the 7 most
// significant frequency number bits, plus a 3-bit
// PM depth value and a signed 5-bit raw PM value,
// return a signed PM adjustment to the frequency;
// algorithm written to match Nuked behavior
//-------------------------------------------------
inline int32_t opn_lfo_pm_phase_adjustment(uint32_t fnum_bits, uint32_t pm_sensitivity, int32_t lfo_raw_pm)
{
// this table encodes 2 shift values to apply to the top 7 bits
// of fnum; it is effectively a cheap multiply by a constant
// value containing 0-2 bits
static uint8_t const s_lfo_pm_shifts[8][8] =
{
{ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 },
{ 0x77, 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x72 },
{ 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x17, 0x17 },
{ 0x77, 0x77, 0x72, 0x72, 0x17, 0x17, 0x12, 0x12 },
{ 0x77, 0x77, 0x72, 0x17, 0x17, 0x17, 0x12, 0x07 },
{ 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 },
{ 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 },
{ 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 }
};
// look up the relevant shifts
int32_t abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm;
uint32_t const shifts = s_lfo_pm_shifts[pm_sensitivity][bitfield(abs_pm, 0, 3)];
// compute the adjustment
int32_t adjust = (fnum_bits >> bitfield(shifts, 0, 4)) + (fnum_bits >> bitfield(shifts, 4, 4));
if (pm_sensitivity > 5)
adjust <<= pm_sensitivity - 5;
adjust >>= 2;
// every 16 cycles it inverts sign
return (lfo_raw_pm < 0) ? -adjust : adjust;
}
//*********************************************************
// FM OPERATOR
//*********************************************************
//-------------------------------------------------
// fm_operator - constructor
//-------------------------------------------------
template<class RegisterType>
fm_operator<RegisterType>::fm_operator(fm_engine_base<RegisterType> &owner, uint32_t opoffs) :
m_choffs(0),
m_opoffs(opoffs),
m_phase(0),
m_env_attenuation(0x3ff),
m_env_state(EG_RELEASE),
m_ssg_inverted(false),
m_key_state(0),
m_keyon_live(0),
m_regs(owner.regs()),
m_owner(owner)
{
}
//-------------------------------------------------
// reset - reset the channel state
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::reset()
{
// reset our data
m_phase = 0;
m_env_attenuation = 0x3ff;
m_env_state = EG_RELEASE;
m_ssg_inverted = 0;
m_key_state = 0;
m_keyon_live = 0;
}
//-------------------------------------------------
// save_restore - save or restore the data
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_phase);
state.save_restore(m_env_attenuation);
state.save_restore(m_env_state);
state.save_restore(m_ssg_inverted);
state.save_restore(m_key_state);
state.save_restore(m_keyon_live);
}
//-------------------------------------------------
// prepare - prepare for clocking
//-------------------------------------------------
template<class RegisterType>
bool fm_operator<RegisterType>::prepare()
{
// cache the data
m_regs.cache_operator_data(m_choffs, m_opoffs, m_cache);
// clock the key state
clock_keystate(uint32_t(m_keyon_live != 0));
m_keyon_live &= ~(1 << KEYON_CSM);
// we're active until we're quiet after the release
return (m_env_state != (RegisterType::EG_HAS_REVERB ? EG_REVERB : EG_RELEASE) || m_env_attenuation < EG_QUIET);
}
//-------------------------------------------------
// clock - master clocking function
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
{
// clock the SSG-EG state (OPN/OPNA)
if (m_regs.op_ssg_eg_enable(m_opoffs))
clock_ssg_eg_state();
else
m_ssg_inverted = false;
// clock the envelope if on an envelope cycle; env_counter is a x.2 value
if (bitfield(env_counter, 0, 2) == 0)
clock_envelope(env_counter >> 2);
// clock the phase
clock_phase(lfo_raw_pm);
}
//-------------------------------------------------
// compute_volume - compute the 14-bit signed
// volume of this operator, given a phase
// modulation and an AM LFO offset
//-------------------------------------------------
template<class RegisterType>
int32_t fm_operator<RegisterType>::compute_volume(uint32_t phase, uint32_t am_offset) const
{
// the low 10 bits of phase represents a full 2*PI period over
// the full sin wave
// early out if the envelope is effectively off
if (m_env_attenuation > EG_QUIET)
return 0;
// get the absolute value of the sin, as attenuation, as a 4.8 fixed point value
uint32_t sin_attenuation = m_cache.waveform[phase & (RegisterType::WAVEFORM_LENGTH - 1)];
// get the attenuation from the evelope generator as a 4.6 value, shifted up to 4.8
uint32_t env_attenuation = envelope_attenuation(am_offset) << 2;
// combine into a 5.8 value, then convert from attenuation to 13-bit linear volume
int32_t result = attenuation_to_volume((sin_attenuation & 0x7fff) + env_attenuation);
// negate if in the negative part of the sin wave (sign bit gives 14 bits)
return bitfield(sin_attenuation, 15) ? -result : result;
}
//-------------------------------------------------
// compute_noise_volume - compute the 14-bit
// signed noise volume of this operator, given a
// noise input value and an AM offset
//-------------------------------------------------
template<class RegisterType>
int32_t fm_operator<RegisterType>::compute_noise_volume(uint32_t am_offset) const
{
// application manual says the logarithmic transform is not applied here, so we
// just use the raw envelope attenuation, inverted (since 0 attenuation should be
// maximum), and shift it up from a 10-bit value to an 11-bit value
int32_t result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1;
// QUESTION: is AM applied still?
// negate based on the noise state
return bitfield(m_regs.noise_state(), 0) ? -result : result;
}
//-------------------------------------------------
// keyonoff - signal a key on/off event
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::keyonoff(uint32_t on, keyon_type type)
{
m_keyon_live = (m_keyon_live & ~(1 << int(type))) | (bitfield(on, 0) << int(type));
}
//-------------------------------------------------
// start_attack - start the attack phase; called
// when a keyon happens or when an SSG-EG cycle
// is complete and restarts
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::start_attack(bool is_restart)
{
// don't change anything if already in attack state
if (m_env_state == EG_ATTACK)
return;
m_env_state = EG_ATTACK;
// generally not inverted at start, except if SSG-EG is enabled and
// one of the inverted modes is specified; leave this alone on a
// restart, as it is managed by the clock_ssg_eg_state() code
if (RegisterType::EG_HAS_SSG && !is_restart)
m_ssg_inverted = m_regs.op_ssg_eg_enable(m_opoffs) & bitfield(m_regs.op_ssg_eg_mode(m_opoffs), 2);
// reset the phase when we start an attack due to a key on
// (but not when due to an SSG-EG restart except in certain cases
// managed directly by the SSG-EG code)
if (!is_restart)
m_phase = 0;
// if the attack rate >= 62 then immediately go to max attenuation
if (m_cache.eg_rate[EG_ATTACK] >= 62)
m_env_attenuation = 0;
}
//-------------------------------------------------
// start_release - start the release phase;
// called when a keyoff happens
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::start_release()
{
// don't change anything if already in release state
if (m_env_state >= EG_RELEASE)
return;
m_env_state = EG_RELEASE;
// if attenuation if inverted due to SSG-EG, snap the inverted attenuation
// as the starting point
if (RegisterType::EG_HAS_SSG && m_ssg_inverted)
{
m_env_attenuation = (0x200 - m_env_attenuation) & 0x3ff;
m_ssg_inverted = false;
}
}
//-------------------------------------------------
// clock_keystate - clock the keystate to match
// the incoming keystate
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::clock_keystate(uint32_t keystate)
{
assert(keystate == 0 || keystate == 1);
// has the key changed?
if ((keystate ^ m_key_state) != 0)
{
m_key_state = keystate;
// if the key has turned on, start the attack
if (keystate != 0)
{
// OPLL has a DP ("depress"?) state to bring the volume
// down before starting the attack
if (RegisterType::EG_HAS_DEPRESS && m_env_attenuation < 0x200)
m_env_state = EG_DEPRESS;
else
start_attack();
}
// otherwise, start the release
else
start_release();
}
}
//-------------------------------------------------
// clock_ssg_eg_state - clock the SSG-EG state;
// should only be called if SSG-EG is enabled
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::clock_ssg_eg_state()
{
// work only happens once the attenuation crosses above 0x200
if (!bitfield(m_env_attenuation, 9))
return;
// 8 SSG-EG modes:
// 000: repeat normally
// 001: run once, hold low
// 010: repeat, alternating between inverted/non-inverted
// 011: run once, hold high
// 100: inverted repeat normally
// 101: inverted run once, hold low
// 110: inverted repeat, alternating between inverted/non-inverted
// 111: inverted run once, hold high
uint32_t mode = m_regs.op_ssg_eg_mode(m_opoffs);
// hold modes (1/3/5/7)
if (bitfield(mode, 0))
{
// set the inverted flag to the end state (0 for modes 1/7, 1 for modes 3/5)
m_ssg_inverted = bitfield(mode, 2) ^ bitfield(mode, 1);
// if holding, force the attenuation to the expected value once we're
// past the attack phase
if (m_env_state != EG_ATTACK)
m_env_attenuation = m_ssg_inverted ? 0x200 : 0x3ff;
}
// continuous modes (0/2/4/6)
else
{
// toggle invert in alternating mode (even in attack state)
m_ssg_inverted ^= bitfield(mode, 1);
// restart attack if in decay/sustain states
if (m_env_state == EG_DECAY || m_env_state == EG_SUSTAIN)
start_attack(true);
// phase is reset to 0 in modes 0/4
if (bitfield(mode, 1) == 0)
m_phase = 0;
}
// in all modes, once we hit release state, attenuation is forced to maximum
if (m_env_state == EG_RELEASE)
m_env_attenuation = 0x3ff;
}
//-------------------------------------------------
// clock_envelope - clock the envelope state
// according to the given count
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::clock_envelope(uint32_t env_counter)
{
// handle attack->decay transitions
if (m_env_state == EG_ATTACK && m_env_attenuation == 0)
m_env_state = EG_DECAY;
// handle decay->sustain transitions; it is important to do this immediately
// after the attack->decay transition above in the event that the sustain level
// is set to 0 (in which case we will skip right to sustain without doing any
// decay); as an example where this can be heard, check the cymbals sound
// in channel 0 of shinobi's test mode sound #5
if (m_env_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain)
m_env_state = EG_SUSTAIN;
// fetch the appropriate 6-bit rate value from the cache
uint32_t rate = m_cache.eg_rate[m_env_state];
// compute the rate shift value; this is the shift needed to
// apply to the env_counter such that it becomes a 5.11 fixed
// point number
uint32_t rate_shift = rate >> 2;
env_counter <<= rate_shift;
// see if the fractional part is 0; if not, it's not time to clock
if (bitfield(env_counter, 0, 11) != 0)
return;
// determine the increment based on the non-fractional part of env_counter
uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3);
uint32_t increment = attenuation_increment(rate, relevant_bits);
// attack is the only one that increases
if (m_env_state == EG_ATTACK)
{
// glitch means that attack rates of 62/63 don't increment if
// changed after the initial key on (where they are handled
// specially); nukeykt confirms this happens on OPM, OPN, OPL/OPLL
// at least so assuming it is true for everyone
if (rate < 62)
m_env_attenuation += (~m_env_attenuation * increment) >> 4;
}
// all other cases are similar
else
{
// non-SSG-EG cases just apply the increment
if (!m_regs.op_ssg_eg_enable(m_opoffs))
m_env_attenuation += increment;
// SSG-EG only applies if less than mid-point, and then at 4x
else if (m_env_attenuation < 0x200)
m_env_attenuation += 4 * increment;
// clamp the final attenuation
if (m_env_attenuation >= 0x400)
m_env_attenuation = 0x3ff;
// transition from depress to attack
if (RegisterType::EG_HAS_DEPRESS && m_env_state == EG_DEPRESS && m_env_attenuation >= 0x200)
start_attack();
// transition from release to reverb, should switch at -18dB
if (RegisterType::EG_HAS_REVERB && m_env_state == EG_RELEASE && m_env_attenuation >= 0xc0)
m_env_state = EG_REVERB;
}
}
//-------------------------------------------------
// clock_phase - clock the 10.10 phase value; the
// OPN version of the logic has been verified
// against the Nuked phase generator
//-------------------------------------------------
template<class RegisterType>
void fm_operator<RegisterType>::clock_phase(int32_t lfo_raw_pm)
{
// read from the cache, or recalculate if PM active
uint32_t phase_step = m_cache.phase_step;
if (phase_step == opdata_cache::PHASE_STEP_DYNAMIC)
phase_step = m_regs.compute_phase_step(m_choffs, m_opoffs, m_cache, lfo_raw_pm);
// finally apply the step to the current phase value
m_phase += phase_step;
}
//-------------------------------------------------
// envelope_attenuation - return the effective
// attenuation of the envelope
//-------------------------------------------------
template<class RegisterType>
uint32_t fm_operator<RegisterType>::envelope_attenuation(uint32_t am_offset) const
{
uint32_t result = m_env_attenuation >> m_cache.eg_shift;
// invert if necessary due to SSG-EG
if (RegisterType::EG_HAS_SSG && m_ssg_inverted)
result = (0x200 - result) & 0x3ff;
// add in LFO AM modulation
if (m_regs.op_lfo_am_enable(m_opoffs))
result += am_offset;
// add in total level and KSL from the cache
result += m_cache.total_level;
// clamp to max, apply shift, and return
return std::min<uint32_t>(result, 0x3ff);
}
//*********************************************************
// FM CHANNEL
//*********************************************************
//-------------------------------------------------
// fm_channel - constructor
//-------------------------------------------------
template<class RegisterType>
fm_channel<RegisterType>::fm_channel(fm_engine_base<RegisterType> &owner, uint32_t choffs) :
m_choffs(choffs),
m_feedback{ 0, 0 },
m_feedback_in(0),
m_op{ nullptr, nullptr, nullptr, nullptr },
m_regs(owner.regs()),
m_owner(owner)
{
}
//-------------------------------------------------
// reset - reset the channel state
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::reset()
{
// reset our data
m_feedback[0] = m_feedback[1] = 0;
m_feedback_in = 0;
}
//-------------------------------------------------
// save_restore - save or restore the data
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_feedback[0]);
state.save_restore(m_feedback[1]);
state.save_restore(m_feedback_in);
}
//-------------------------------------------------
// keyonoff - signal key on/off to our operators
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::keyonoff(uint32_t states, keyon_type type, uint32_t chnum)
{
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
if (m_op[opnum] != nullptr)
m_op[opnum]->keyonoff(bitfield(states, opnum), type);
if (debug::LOG_KEYON_EVENTS && ((debug::GLOBAL_FM_CHANNEL_MASK >> chnum) & 1) != 0)
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
if (m_op[opnum] != nullptr)
debug::log_keyon("%c%s\n", bitfield(states, opnum) ? '+' : '-', m_regs.log_keyon(m_choffs, m_op[opnum]->opoffs()).c_str());
}
//-------------------------------------------------
// prepare - prepare for clocking
//-------------------------------------------------
template<class RegisterType>
bool fm_channel<RegisterType>::prepare()
{
uint32_t active_mask = 0;
// prepare all operators and determine if they are active
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
if (m_op[opnum] != nullptr)
if (m_op[opnum]->prepare())
active_mask |= 1 << opnum;
return (active_mask != 0);
}
//-------------------------------------------------
// clock - master clock of all operators
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
{
// clock the feedback through
m_feedback[0] = m_feedback[1];
m_feedback[1] = m_feedback_in;
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
if (m_op[opnum] != nullptr)
m_op[opnum]->clock(env_counter, lfo_raw_pm);
/*
useful temporary code for envelope debugging
if (m_choffs == 0x101)
{
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
{
auto &op = *m_op[((opnum & 1) << 1) | ((opnum >> 1) & 1)];
printf(" %c%03X%c%c ",
"PADSRV"[op.debug_eg_state()],
op.debug_eg_attenuation(),
op.debug_ssg_inverted() ? '-' : '+',
m_regs.op_ssg_eg_enable(op.opoffs()) ? '0' + m_regs.op_ssg_eg_mode(op.opoffs()) : ' ');
}
printf(" -- ");
}
*/
}
//-------------------------------------------------
// output_2op - combine 4 operators according to
// the specified algorithm, returning a sum
// according to the rshift and clipmax parameters,
// which vary between different implementations
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::output_2op(output_data &output, uint32_t rshift, int32_t clipmax) const
{
// The first 2 operators should be populated
assert(m_op[0] != nullptr);
assert(m_op[1] != nullptr);
// AM amount is the same across all operators; compute it once
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
// operator 1 has optional self-feedback
int32_t opmod = 0;
uint32_t feedback = m_regs.ch_feedback(m_choffs);
if (feedback != 0)
opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
// compute the 14-bit volume/value of operator 1 and update the feedback
int32_t op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
// now that the feedback has been computed, skip the rest if all volumes
// are clear; no need to do all this work for nothing
if (m_regs.ch_output_any(m_choffs) == 0)
return;
// Algorithms for two-operator case:
// 0: O1 -> O2 -> out
// 1: (O1 + O2) -> out
int32_t result;
if (bitfield(m_regs.ch_algorithm(m_choffs), 0) == 0)
{
// some OPL chips use the previous sample for modulation instead of
// the current sample
opmod = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> 1;
result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift;
}
else
{
result = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> rshift;
result += m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift;
int32_t clipmin = -clipmax - 1;
result = clamp(result, clipmin, clipmax);
}
// add to the output
add_to_output(m_choffs, output, result);
}
//-------------------------------------------------
// output_4op - combine 4 operators according to
// the specified algorithm, returning a sum
// according to the rshift and clipmax parameters,
// which vary between different implementations
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::output_4op(output_data &output, uint32_t rshift, int32_t clipmax) const
{
// all 4 operators should be populated
assert(m_op[0] != nullptr);
assert(m_op[1] != nullptr);
assert(m_op[2] != nullptr);
assert(m_op[3] != nullptr);
// AM amount is the same across all operators; compute it once
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
// operator 1 has optional self-feedback
int32_t opmod = 0;
uint32_t feedback = m_regs.ch_feedback(m_choffs);
if (feedback != 0)
opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
// compute the 14-bit volume/value of operator 1 and update the feedback
int32_t op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
// now that the feedback has been computed, skip the rest if all volumes
// are clear; no need to do all this work for nothing
if (m_regs.ch_output_any(m_choffs) == 0)
return;
// OPM/OPN offer 8 different connection algorithms for 4 operators,
// and OPL3 offers 4 more, which we designate here as 8-11.
//
// The operators are computed in order, with the inputs pulled from
// an array of values (opout) that is populated as we go:
// 0 = 0
// 1 = O1
// 2 = O2
// 3 = O3
// 4 = (O4)
// 5 = O1+O2
// 6 = O1+O3
// 7 = O2+O3
//
// The s_algorithm_ops table describes the inputs and outputs of each
// algorithm as follows:
//
// ---------x use opout[x] as operator 2 input
// ------xxx- use opout[x] as operator 3 input
// ---xxx---- use opout[x] as operator 4 input
// --x------- include opout[1] in final sum
// -x-------- include opout[2] in final sum
// x--------- include opout[3] in final sum
#define ALGORITHM(op2in, op3in, op4in, op1out, op2out, op3out) \
((op2in) | ((op3in) << 1) | ((op4in) << 4) | ((op1out) << 7) | ((op2out) << 8) | ((op3out) << 9))
static uint16_t const s_algorithm_ops[8+4] =
{
ALGORITHM(1,2,3, 0,0,0), // 0: O1 -> O2 -> O3 -> O4 -> out (O4)
ALGORITHM(0,5,3, 0,0,0), // 1: (O1 + O2) -> O3 -> O4 -> out (O4)
ALGORITHM(0,2,6, 0,0,0), // 2: (O1 + (O2 -> O3)) -> O4 -> out (O4)
ALGORITHM(1,0,7, 0,0,0), // 3: ((O1 -> O2) + O3) -> O4 -> out (O4)
ALGORITHM(1,0,3, 0,1,0), // 4: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4)
ALGORITHM(1,1,1, 0,1,1), // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out (O2+O3+O4)
ALGORITHM(1,0,0, 0,1,1), // 6: ((O1 -> O2) + O3 + O4) -> out (O2+O3+O4)
ALGORITHM(0,0,0, 1,1,1), // 7: (O1 + O2 + O3 + O4) -> out (O1+O2+O3+O4)
ALGORITHM(1,2,3, 0,0,0), // 8: O1 -> O2 -> O3 -> O4 -> out (O4) [same as 0]
ALGORITHM(0,2,3, 1,0,0), // 9: (O1 + (O2 -> O3 -> O4)) -> out (O1+O4) [unique]
ALGORITHM(1,0,3, 0,1,0), // 10: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) [same as 4]
ALGORITHM(0,2,0, 1,0,1) // 11: (O1 + (O2 -> O3) + O4) -> out (O1+O3+O4) [unique]
};
uint32_t algorithm_ops = s_algorithm_ops[m_regs.ch_algorithm(m_choffs)];
// populate the opout table
int16_t opout[8];
opout[0] = 0;
opout[1] = op1value;
// compute the 14-bit volume/value of operator 2
opmod = opout[bitfield(algorithm_ops, 0, 1)] >> 1;
opout[2] = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset);
opout[5] = opout[1] + opout[2];
// compute the 14-bit volume/value of operator 3
opmod = opout[bitfield(algorithm_ops, 1, 3)] >> 1;
opout[3] = m_op[2]->compute_volume(m_op[2]->phase() + opmod, am_offset);
opout[6] = opout[1] + opout[3];
opout[7] = opout[2] + opout[3];
// compute the 14-bit volume/value of operator 4; this could be a noise
// value on the OPM; all algorithms consume OP4 output at a minimum
int32_t result;
if (m_regs.noise_enable() && m_choffs == 7)
result = m_op[3]->compute_noise_volume(am_offset);
else
{
opmod = opout[bitfield(algorithm_ops, 4, 3)] >> 1;
result = m_op[3]->compute_volume(m_op[3]->phase() + opmod, am_offset);
}
result >>= rshift;
// optionally add OP1, OP2, OP3
int32_t clipmin = -clipmax - 1;
if (bitfield(algorithm_ops, 7) != 0)
result = clamp(result + (opout[1] >> rshift), clipmin, clipmax);
if (bitfield(algorithm_ops, 8) != 0)
result = clamp(result + (opout[2] >> rshift), clipmin, clipmax);
if (bitfield(algorithm_ops, 9) != 0)
result = clamp(result + (opout[3] >> rshift), clipmin, clipmax);
// add to the output
add_to_output(m_choffs, output, result);
}
//-------------------------------------------------
// output_rhythm_ch6 - special case output
// computation for OPL channel 6 in rhythm mode,
// which outputs a Bass Drum instrument
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::output_rhythm_ch6(output_data &output, uint32_t rshift, int32_t clipmax) const
{
// AM amount is the same across all operators; compute it once
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
// Bass Drum: this uses operators 12 and 15 (i.e., channel 6)
// in an almost-normal way, except that if the algorithm is 1,
// the first operator is ignored instead of added in
// operator 1 has optional self-feedback
int32_t opmod = 0;
uint32_t feedback = m_regs.ch_feedback(m_choffs);
if (feedback != 0)
opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
// compute the 14-bit volume/value of operator 1 and update the feedback
int32_t opout1 = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
// compute the 14-bit volume/value of operator 2, which is the result
opmod = bitfield(m_regs.ch_algorithm(m_choffs), 0) ? 0 : (opout1 >> 1);
int32_t result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift;
// add to the output
add_to_output(m_choffs, output, result * 2);
}
//-------------------------------------------------
// output_rhythm_ch7 - special case output
// computation for OPL channel 7 in rhythm mode,
// which outputs High Hat and Snare Drum
// instruments
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::output_rhythm_ch7(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const
{
// AM amount is the same across all operators; compute it once
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
uint32_t noise_state = bitfield(m_regs.noise_state(), 0);
// High Hat: this uses the envelope from operator 13 (channel 7),
// and a combination of noise and the operator 13/17 phase select
// to compute the phase
uint32_t phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select)));
int32_t result = m_op[0]->compute_volume(phase, am_offset) >> rshift;
// Snare Drum: this uses the envelope from operator 16 (channel 7),
// and a combination of noise and operator 13 phase to pick a phase
uint32_t op13phase = m_op[0]->phase();
phase = (0x100 << bitfield(op13phase, 8)) ^ (noise_state << 8);
result += m_op[1]->compute_volume(phase, am_offset) >> rshift;
result = clamp(result, -clipmax - 1, clipmax);
// add to the output
add_to_output(m_choffs, output, result * 2);
}
//-------------------------------------------------
// output_rhythm_ch8 - special case output
// computation for OPL channel 8 in rhythm mode,
// which outputs Tom Tom and Top Cymbal instruments
//-------------------------------------------------
template<class RegisterType>
void fm_channel<RegisterType>::output_rhythm_ch8(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const
{
// AM amount is the same across all operators; compute it once
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
// Tom Tom: this is just a single operator processed normally
int32_t result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift;
// Top Cymbal: this uses the envelope from operator 17 (channel 8),
// and the operator 13/17 phase select to compute the phase
uint32_t phase = 0x100 | (phase_select << 9);
result += m_op[1]->compute_volume(phase, am_offset) >> rshift;
result = clamp(result, -clipmax - 1, clipmax);
// add to the output
add_to_output(m_choffs, output, result * 2);
}
//*********************************************************
// FM ENGINE BASE
//*********************************************************
//-------------------------------------------------
// fm_engine_base - constructor
//-------------------------------------------------
template<class RegisterType>
fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
m_intf(intf),
m_env_counter(0),
m_status(0),
m_clock_prescale(RegisterType::DEFAULT_PRESCALE),
m_irq_mask(STATUS_TIMERA | STATUS_TIMERB),
m_irq_state(0),
m_timer_running{0,0},
m_total_clocks(0),
m_active_channels(ALL_CHANNELS),
m_modified_channels(ALL_CHANNELS),
m_prepare_count(0)
{
// inform the interface of their engine
m_intf.m_engine = this;
// create the channels
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
m_channel[chnum] = std::make_unique<fm_channel<RegisterType>>(*this, RegisterType::channel_offset(chnum));
// create the operators
for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
m_operator[opnum] = std::make_unique<fm_operator<RegisterType>>(*this, RegisterType::operator_offset(opnum));
#if (YMFM_DEBUG_LOG_WAVFILES)
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
m_wavfile[chnum].set_index(chnum);
#endif
// do the initial operator assignment
assign_operators();
}
//-------------------------------------------------
// reset - reset the overall state
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::reset()
{
// reset all status bits
set_reset_status(0, 0xff);
// register type-specific initialization
m_regs.reset();
// explicitly write to the mode register since it has side-effects
// QUESTION: old cores initialize this to 0x30 -- who is right?
write(RegisterType::REG_MODE, 0);
// reset the channels
for (auto &chan : m_channel)
chan->reset();
// reset the operators
for (auto &op : m_operator)
op->reset();
}
//-------------------------------------------------
// save_restore - save or restore the data
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::save_restore(ymfm_saved_state &state)
{
// save our data
state.save_restore(m_env_counter);
state.save_restore(m_status);
state.save_restore(m_clock_prescale);
state.save_restore(m_irq_mask);
state.save_restore(m_irq_state);
state.save_restore(m_timer_running[0]);
state.save_restore(m_timer_running[1]);
state.save_restore(m_total_clocks);
// save the register/family data
m_regs.save_restore(state);
// save channel data
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
m_channel[chnum]->save_restore(state);
// save operator data
for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
m_operator[opnum]->save_restore(state);
// invalidate any caches
invalidate_caches();
}
//-------------------------------------------------
// clock - iterate over all channels, clocking
// them forward one step
//-------------------------------------------------
template<class RegisterType>
uint32_t fm_engine_base<RegisterType>::clock(uint32_t chanmask)
{
// update the clock counter
m_total_clocks++;
// if something was modified, prepare
// also prepare every 4k samples to catch ending notes
if (m_modified_channels != 0 || m_prepare_count++ >= 4096)
{
// reassign operators to channels if dynamic
if (RegisterType::DYNAMIC_OPS)
assign_operators();
// call each channel to prepare
m_active_channels = 0;
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
if (bitfield(chanmask, chnum))
if (m_channel[chnum]->prepare())
m_active_channels |= 1 << chnum;
// reset the modified channels and prepare count
m_modified_channels = m_prepare_count = 0;
}
// if the envelope clock divider is 1, just increment by 4;
// otherwise, increment by 1 and manually wrap when we reach the divide count
if (RegisterType::EG_CLOCK_DIVIDER == 1)
m_env_counter += 4;
else if (bitfield(++m_env_counter, 0, 2) == RegisterType::EG_CLOCK_DIVIDER)
m_env_counter += 4 - RegisterType::EG_CLOCK_DIVIDER;
// clock the noise generator
int32_t lfo_raw_pm = m_regs.clock_noise_and_lfo();
// now update the state of all the channels and operators
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
if (bitfield(chanmask, chnum))
m_channel[chnum]->clock(m_env_counter, lfo_raw_pm);
// return the envelope counter as it is used to clock ADPCM-A
return m_env_counter;
}
//-------------------------------------------------
// output - compute a sum over the relevant
// channels
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift, int32_t clipmax, uint32_t chanmask) const
{
// mask out some channels for debug purposes
chanmask &= debug::GLOBAL_FM_CHANNEL_MASK;
// mask out inactive channels
if (!YMFM_DEBUG_LOG_WAVFILES)
chanmask &= m_active_channels;
// handle the rhythm case, where some of the operators are dedicated
// to percussion (this is an OPL-specific feature)
if (m_regs.rhythm_enable())
{
// we don't support the OPM noise channel here; ensure it is off
assert(m_regs.noise_enable() == 0);
// precompute the operator 13+17 phase selection value
uint32_t op13phase = m_operator[13]->phase();
uint32_t op17phase = m_operator[17]->phase();
uint32_t phase_select = (bitfield(op13phase, 2) ^ bitfield(op13phase, 7)) | bitfield(op13phase, 3) | (bitfield(op17phase, 5) ^ bitfield(op17phase, 3));
// sum over all the desired channels
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
if (bitfield(chanmask, chnum))
{
#if (YMFM_DEBUG_LOG_WAVFILES)
auto reference = output;
#endif
if (chnum == 6)
m_channel[chnum]->output_rhythm_ch6(output, rshift, clipmax);
else if (chnum == 7)
m_channel[chnum]->output_rhythm_ch7(phase_select, output, rshift, clipmax);
else if (chnum == 8)
m_channel[chnum]->output_rhythm_ch8(phase_select, output, rshift, clipmax);
else if (m_channel[chnum]->is4op())
m_channel[chnum]->output_4op(output, rshift, clipmax);
else
m_channel[chnum]->output_2op(output, rshift, clipmax);
#if (YMFM_DEBUG_LOG_WAVFILES)
m_wavfile[chnum].add(output, reference);
#endif
}
}
else
{
// sum over all the desired channels
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
if (bitfield(chanmask, chnum))
{
#if (YMFM_DEBUG_LOG_WAVFILES)
auto reference = output;
#endif
if (m_channel[chnum]->is4op())
m_channel[chnum]->output_4op(output, rshift, clipmax);
else
m_channel[chnum]->output_2op(output, rshift, clipmax);
#if (YMFM_DEBUG_LOG_WAVFILES)
m_wavfile[chnum].add(output, reference);
#endif
}
}
}
//-------------------------------------------------
// write - handle writes to the OPN registers
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::write(uint16_t regnum, uint8_t data)
{
debug::log_fm_write("%03X = %02X\n", regnum, data);
// special case: writes to the mode register can impact IRQs;
// schedule these writes to ensure ordering with timers
if (regnum == RegisterType::REG_MODE)
{
m_intf.ymfm_sync_mode_write(data);
return;
}
// for now just mark all channels as modified
m_modified_channels = ALL_CHANNELS;
// most writes are passive, consumed only when needed
uint32_t keyon_channel;
uint32_t keyon_opmask;
if (m_regs.write(regnum, data, keyon_channel, keyon_opmask))
{
// handle writes to the keyon register(s)
if (keyon_channel < CHANNELS)
{
// normal channel on/off
m_channel[keyon_channel]->keyonoff(keyon_opmask, KEYON_NORMAL, keyon_channel);
}
else if (CHANNELS >= 9 && keyon_channel == RegisterType::RHYTHM_CHANNEL)
{
// special case for the OPL rhythm channels
m_channel[6]->keyonoff(bitfield(keyon_opmask, 4) ? 3 : 0, KEYON_RHYTHM, 6);
m_channel[7]->keyonoff(bitfield(keyon_opmask, 0) | (bitfield(keyon_opmask, 3) << 1), KEYON_RHYTHM, 7);
m_channel[8]->keyonoff(bitfield(keyon_opmask, 2) | (bitfield(keyon_opmask, 1) << 1), KEYON_RHYTHM, 8);
}
}
}
//-------------------------------------------------
// status - return the current state of the
// status flags
//-------------------------------------------------
template<class RegisterType>
uint8_t fm_engine_base<RegisterType>::status() const
{
return m_status & ~STATUS_BUSY & ~m_regs.status_mask();
}
//-------------------------------------------------
// assign_operators - get the current mapping of
// operators to channels and assign them all
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::assign_operators()
{
typename RegisterType::operator_mapping map;
m_regs.operator_map(map);
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
for (uint32_t index = 0; index < 4; index++)
{
uint32_t opnum = bitfield(map.chan[chnum], 8 * index, 8);
m_channel[chnum]->assign(index, (opnum == 0xff) ? nullptr : m_operator[opnum].get());
}
}
//-------------------------------------------------
// update_timer - update the state of the given
// timer
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::update_timer(uint32_t tnum, uint32_t enable, int32_t delta_clocks)
{
// if the timer is live, but not currently enabled, set the timer
if (enable && !m_timer_running[tnum])
{
// period comes from the registers, and is different for each
uint32_t period = (tnum == 0) ? (1024 - m_regs.timer_a_value()) : 16 * (256 - m_regs.timer_b_value());
// caller can also specify a delta to account for other effects
period += delta_clocks;
// reset it
m_intf.ymfm_set_timer(tnum, period * OPERATORS * m_clock_prescale);
m_timer_running[tnum] = 1;
}
// if the timer is not live, ensure it is not enabled
else if (!enable)
{
m_intf.ymfm_set_timer(tnum, -1);
m_timer_running[tnum] = 0;
}
}
//-------------------------------------------------
// engine_timer_expired - timer has expired - signal
// status and possibly IRQs
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::engine_timer_expired(uint32_t tnum)
{
// update status
if (tnum == 0 && m_regs.enable_timer_a())
set_reset_status(STATUS_TIMERA, 0);
else if (tnum == 1 && m_regs.enable_timer_b())
set_reset_status(STATUS_TIMERB, 0);
// if timer A fired in CSM mode, trigger CSM on all relevant channels
if (tnum == 0 && m_regs.csm())
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum))
{
m_channel[chnum]->keyonoff(1, KEYON_CSM, chnum);
m_modified_channels |= 1 << chnum;
}
// reset
m_timer_running[tnum] = false;
update_timer(tnum, 1, 0);
}
//-------------------------------------------------
// check_interrupts - check the interrupt sources
// for interrupts
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::engine_check_interrupts()
{
// update the state
uint8_t old_state = m_irq_state;
m_irq_state = ((m_status & m_irq_mask & ~m_regs.status_mask()) != 0);
// set the IRQ status bit
if (m_irq_state)
m_status |= STATUS_IRQ;
else
m_status &= ~STATUS_IRQ;
// if changed, signal the new state
if (old_state != m_irq_state)
m_intf.ymfm_update_irq(m_irq_state ? true : false);
}
//-------------------------------------------------
// engine_mode_write - handle a mode register write
// via timer callback
//-------------------------------------------------
template<class RegisterType>
void fm_engine_base<RegisterType>::engine_mode_write(uint8_t data)
{
// mark all channels as modified
m_modified_channels = ALL_CHANNELS;
// actually write the mode register now
uint32_t dummy1, dummy2;
m_regs.write(RegisterType::REG_MODE, data, dummy1, dummy2);
// reset IRQ status -- when written, all other bits are ignored
// QUESTION: should this maybe just reset the IRQ bit and not all the bits?
// That is, check_interrupts would only set, this would only clear?
if (m_regs.irq_reset())
set_reset_status(0, 0x78);
else
{
// reset timer status
uint8_t reset_mask = 0;
if (m_regs.reset_timer_b())
reset_mask |= RegisterType::STATUS_TIMERB;
if (m_regs.reset_timer_a())
reset_mask |= RegisterType::STATUS_TIMERA;
set_reset_status(0, reset_mask);
// load timers; note that timer B gets a small negative adjustment because
// the *16 multiplier is free-running, so the first tick of the clock
// is a bit shorter
update_timer(1, m_regs.load_timer_b(), -(m_total_clocks & 15));
update_timer(0, m_regs.load_timer_a(), 0);
}
}
}