// 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 fm_operator::fm_operator(fm_engine_base &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 void fm_operator::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 void fm_operator::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 bool fm_operator::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 void fm_operator::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 int32_t fm_operator::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 int32_t fm_operator::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 void fm_operator::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 void fm_operator::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 void fm_operator::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 void fm_operator::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 void fm_operator::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 void fm_operator::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 void fm_operator::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 uint32_t fm_operator::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(result, 0x3ff); } //********************************************************* // FM CHANNEL //********************************************************* //------------------------------------------------- // fm_channel - constructor //------------------------------------------------- template fm_channel::fm_channel(fm_engine_base &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 void fm_channel::reset() { // reset our data m_feedback[0] = m_feedback[1] = 0; m_feedback_in = 0; } //------------------------------------------------- // save_restore - save or restore the data //------------------------------------------------- template void fm_channel::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 void fm_channel::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 bool fm_channel::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 void fm_channel::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 void fm_channel::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 void fm_channel::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 void fm_channel::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 void fm_channel::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 void fm_channel::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 fm_engine_base::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>(*this, RegisterType::channel_offset(chnum)); // create the operators for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) m_operator[opnum] = std::make_unique>(*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 void fm_engine_base::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 void fm_engine_base::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 uint32_t fm_engine_base::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 void fm_engine_base::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 void fm_engine_base::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 uint8_t fm_engine_base::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 void fm_engine_base::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 void fm_engine_base::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 void fm_engine_base::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 void fm_engine_base::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 void fm_engine_base::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); } } }