aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi_opl3.cpp
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2020-09-13 15:58:37 +0300
committerWohlstand <admin@wohlnet.ru>2020-09-13 15:58:37 +0300
commit7f66550168af30e03b7762ba9ce6a9d56f578064 (patch)
tree37e436127c4701902b63ec2d360797d16aeedda8 /src/adlmidi_opl3.cpp
parente6806ad8a4ce1c91530e031b1aac1d89c68b9481 (diff)
downloadlibADLMIDI-7f66550168af30e03b7762ba9ce6a9d56f578064.tar.gz
libADLMIDI-7f66550168af30e03b7762ba9ce6a9d56f578064.tar.bz2
libADLMIDI-7f66550168af30e03b7762ba9ce6a9d56f578064.zip
Move all frequency computation code from a MIDIPlay into the Synth
Diffstat (limited to 'src/adlmidi_opl3.cpp')
-rw-r--r--src/adlmidi_opl3.cpp660
1 files changed, 637 insertions, 23 deletions
diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp
index 1185768..523ab53 100644
--- a/src/adlmidi_opl3.cpp
+++ b/src/adlmidi_opl3.cpp
@@ -195,6 +195,14 @@ static const uint16_t g_channelsMapPan[NUM_OF_CHANNELS] =
Ports: ???
*/
+
+
+
+
+/***************************************************************
+ * Volume model tables *
+ ***************************************************************/
+
// Mapping from MIDI volume level to OPL level value.
static const uint_fast32_t s_dmx_volume_model[128] =
@@ -251,6 +259,580 @@ static const uint_fast32_t s_hmi_volume_table[64] =
0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00,
};
+
+
+
+
+/***************************************************************
+ * Standard frequency formula *
+ * *************************************************************/
+
+static inline double s_commonFreq(double tone)
+{
+ return BEND_COEFFICIENT * std::exp(0.057762265 * tone);
+}
+
+
+
+
+/***************************************************************
+ * DMX frequency model *
+ * *************************************************************/
+
+// DMX volumes table
+static const int_fast32_t s_dmx_freq_table[] =
+{
+ 0x0133, 0x0133, 0x0134, 0x0134, 0x0135, 0x0136, 0x0136, 0x0137,
+ 0x0137, 0x0138, 0x0138, 0x0139, 0x0139, 0x013A, 0x013B, 0x013B,
+ 0x013C, 0x013C, 0x013D, 0x013D, 0x013E, 0x013F, 0x013F, 0x0140,
+ 0x0140, 0x0141, 0x0142, 0x0142, 0x0143, 0x0143, 0x0144, 0x0144,
+
+ 0x0145, 0x0146, 0x0146, 0x0147, 0x0147, 0x0148, 0x0149, 0x0149,
+ 0x014A, 0x014A, 0x014B, 0x014C, 0x014C, 0x014D, 0x014D, 0x014E,
+ 0x014F, 0x014F, 0x0150, 0x0150, 0x0151, 0x0152, 0x0152, 0x0153,
+ 0x0153, 0x0154, 0x0155, 0x0155, 0x0156, 0x0157, 0x0157, 0x0158,
+
+ 0x0158, 0x0159, 0x015A, 0x015A, 0x015B, 0x015B, 0x015C, 0x015D,
+ 0x015D, 0x015E, 0x015F, 0x015F, 0x0160, 0x0161, 0x0161, 0x0162,
+ 0x0162, 0x0163, 0x0164, 0x0164, 0x0165, 0x0166, 0x0166, 0x0167,
+ 0x0168, 0x0168, 0x0169, 0x016A, 0x016A, 0x016B, 0x016C, 0x016C,
+
+ 0x016D, 0x016E, 0x016E, 0x016F, 0x0170, 0x0170, 0x0171, 0x0172,
+ 0x0172, 0x0173, 0x0174, 0x0174, 0x0175, 0x0176, 0x0176, 0x0177,
+ 0x0178, 0x0178, 0x0179, 0x017A, 0x017A, 0x017B, 0x017C, 0x017C,
+ 0x017D, 0x017E, 0x017E, 0x017F, 0x0180, 0x0181, 0x0181, 0x0182,
+
+ 0x0183, 0x0183, 0x0184, 0x0185, 0x0185, 0x0186, 0x0187, 0x0188,
+ 0x0188, 0x0189, 0x018A, 0x018A, 0x018B, 0x018C, 0x018D, 0x018D,
+ 0x018E, 0x018F, 0x018F, 0x0190, 0x0191, 0x0192, 0x0192, 0x0193,
+ 0x0194, 0x0194, 0x0195, 0x0196, 0x0197, 0x0197, 0x0198, 0x0199,
+
+ 0x019A, 0x019A, 0x019B, 0x019C, 0x019D, 0x019D, 0x019E, 0x019F,
+ 0x01A0, 0x01A0, 0x01A1, 0x01A2, 0x01A3, 0x01A3, 0x01A4, 0x01A5,
+ 0x01A6, 0x01A6, 0x01A7, 0x01A8, 0x01A9, 0x01A9, 0x01AA, 0x01AB,
+ 0x01AC, 0x01AD, 0x01AD, 0x01AE, 0x01AF, 0x01B0, 0x01B0, 0x01B1,
+
+ 0x01B2, 0x01B3, 0x01B4, 0x01B4, 0x01B5, 0x01B6, 0x01B7, 0x01B8,
+ 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BC, 0x01BD, 0x01BE,
+ 0x01BF, 0x01C0, 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C4, 0x01C4,
+ 0x01C5, 0x01C6, 0x01C7, 0x01C8, 0x01C9, 0x01C9, 0x01CA, 0x01CB,
+
+ 0x01CC, 0x01CD, 0x01CE, 0x01CE, 0x01CF, 0x01D0, 0x01D1, 0x01D2,
+ 0x01D3, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, 0x01D8, 0x01D8,
+ 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DE, 0x01DF,
+ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E4, 0x01E5, 0x01E5, 0x01E6,
+
+ 0x01E7, 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01ED,
+ 0x01EE, 0x01EF, 0x01F0, 0x01F1, 0x01F2, 0x01F3, 0x01F4, 0x01F5,
+ 0x01F6, 0x01F6, 0x01F7, 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC,
+ 0x01FD, 0x01FE, 0x01FF,
+
+ 0x0200, 0x0201, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206,
+ 0x0207, 0x0208, 0x0209, 0x020A, 0x020B, 0x020C, 0x020D, 0x020E,
+ 0x020F, 0x0210, 0x0210, 0x0211, 0x0212, 0x0213, 0x0214, 0x0215,
+ 0x0216, 0x0217, 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D,
+
+ 0x021E, 0x021F, 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225,
+ 0x0226, 0x0227, 0x0228, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D,
+ 0x022E, 0x022F, 0x0230, 0x0231, 0x0232, 0x0233, 0x0234, 0x0235,
+ 0x0236, 0x0237, 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D,
+
+ 0x023E, 0x023F, 0x0240, 0x0241, 0x0242, 0x0244, 0x0245, 0x0246,
+ 0x0247, 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E,
+ 0x024F, 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0256, 0x0257,
+ 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F,
+
+ 0x0260, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, 0x0268,
+ 0x0269, 0x026A, 0x026C, 0x026D, 0x026E, 0x026F, 0x0270, 0x0271,
+ 0x0272, 0x0273, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279, 0x027A,
+ 0x027B, 0x027D, 0x027E, 0x027F, 0x0280, 0x0281, 0x0282, 0x0284,
+
+ 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028B, 0x028C, 0x028D,
+ 0x028E, 0x028F, 0x0290, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296,
+ 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029E, 0x029F, 0x02A0,
+ 0x02A1, 0x02A2, 0x02A4, 0x02A5, 0x02A6, 0x02A7, 0x02A9, 0x02AA,
+
+ 0x02AB, 0x02AC, 0x02AE, 0x02AF, 0x02B0, 0x02B1, 0x02B2, 0x02B4,
+ 0x02B5, 0x02B6, 0x02B7, 0x02B9, 0x02BA, 0x02BB, 0x02BD, 0x02BE,
+ 0x02BF, 0x02C0, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C7, 0x02C8,
+ 0x02C9, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02D0, 0x02D1, 0x02D2,
+
+ 0x02D4, 0x02D5, 0x02D6, 0x02D8, 0x02D9, 0x02DA, 0x02DC, 0x02DD,
+ 0x02DE, 0x02E0, 0x02E1, 0x02E2, 0x02E4, 0x02E5, 0x02E6, 0x02E8,
+ 0x02E9, 0x02EA, 0x02EC, 0x02ED, 0x02EE, 0x02F0, 0x02F1, 0x02F2,
+ 0x02F4, 0x02F5, 0x02F6, 0x02F8, 0x02F9, 0x02FB, 0x02FC, 0x02FD,
+
+ 0x02FF, 0x0300, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307, 0x0309,
+ 0x030A, 0x030B, 0x030D, 0x030E, 0x0310, 0x0311, 0x0312, 0x0314,
+ 0x0315, 0x0317, 0x0318, 0x031A, 0x031B, 0x031C, 0x031E, 0x031F,
+ 0x0321, 0x0322, 0x0324, 0x0325, 0x0327, 0x0328, 0x0329, 0x032B,
+
+ 0x032C, 0x032E, 0x032F, 0x0331, 0x0332, 0x0334, 0x0335, 0x0337,
+ 0x0338, 0x033A, 0x033B, 0x033D, 0x033E, 0x0340, 0x0341, 0x0343,
+ 0x0344, 0x0346, 0x0347, 0x0349, 0x034A, 0x034C, 0x034D, 0x034F,
+ 0x0350, 0x0352, 0x0353, 0x0355, 0x0357, 0x0358, 0x035A, 0x035B,
+
+ 0x035D, 0x035E, 0x0360, 0x0361, 0x0363, 0x0365, 0x0366, 0x0368,
+ 0x0369, 0x036B, 0x036C, 0x036E, 0x0370, 0x0371, 0x0373, 0x0374,
+ 0x0376, 0x0378, 0x0379, 0x037B, 0x037C, 0x037E, 0x0380, 0x0381,
+ 0x0383, 0x0384, 0x0386, 0x0388, 0x0389, 0x038B, 0x038D, 0x038E,
+
+ 0x0390, 0x0392, 0x0393, 0x0395, 0x0397, 0x0398, 0x039A, 0x039C,
+ 0x039D, 0x039F, 0x03A1, 0x03A2, 0x03A4, 0x03A6, 0x03A7, 0x03A9,
+ 0x03AB, 0x03AC, 0x03AE, 0x03B0, 0x03B1, 0x03B3, 0x03B5, 0x03B7,
+ 0x03B8, 0x03BA, 0x03BC, 0x03BD, 0x03BF, 0x03C1, 0x03C3, 0x03C4,
+
+ 0x03C6, 0x03C8, 0x03CA, 0x03CB, 0x03CD, 0x03CF, 0x03D1, 0x03D2,
+ 0x03D4, 0x03D6, 0x03D8, 0x03DA, 0x03DB, 0x03DD, 0x03DF, 0x03E1,
+ 0x03E3, 0x03E4, 0x03E6, 0x03E8, 0x03EA, 0x03EC, 0x03ED, 0x03EF,
+ 0x03F1, 0x03F3, 0x03F5, 0x03F6, 0x03F8, 0x03FA, 0x03FC, 0x03FE,
+
+ 0x036C
+};
+
+static inline double s_dmxFreq(double tone)
+{
+ uint_fast32_t noteI = (uint_fast32_t)(tone);
+ int_fast32_t bendI = 0;
+ int_fast32_t outHz = 0.0;
+ double bendDec = tone - (int)tone;
+
+ bendI = (int_fast32_t)((bendDec * 128.0) / 2.0) + 128;
+ bendI = bendI >> 1;
+
+ int_fast32_t oct = 0;
+ int_fast32_t freqIndex = (noteI << 5) + bendI;
+
+ if(freqIndex < 0)
+ freqIndex = 0;
+ else if(freqIndex >= 284)
+ {
+ freqIndex -= 284;
+ oct = freqIndex / 384;
+ freqIndex = (freqIndex % 384) + 284;
+ }
+
+ outHz = s_dmx_freq_table[freqIndex];
+
+ while(oct > 1)
+ {
+ outHz *= 2;
+ oct -= 1;
+ }
+
+ return (double)outHz;
+}
+
+
+
+
+/***************************************************************
+ * Apogee Sound System frequency model *
+ ***************************************************************/
+
+static const int_fast32_t s_apogee_freq_table[31 + 1][12] =
+{
+ { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287 },
+ { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x242, 0x264, 0x288 },
+ { 0x158, 0x16c, 0x182, 0x199, 0x1b1, 0x1cb, 0x1e6, 0x203, 0x221, 0x243, 0x265, 0x289 },
+ { 0x158, 0x16c, 0x183, 0x19a, 0x1b2, 0x1cc, 0x1e7, 0x204, 0x222, 0x244, 0x266, 0x28a },
+ { 0x159, 0x16d, 0x183, 0x19a, 0x1b3, 0x1cd, 0x1e8, 0x205, 0x223, 0x245, 0x267, 0x28b },
+ { 0x15a, 0x16e, 0x184, 0x19b, 0x1b3, 0x1ce, 0x1e9, 0x206, 0x224, 0x246, 0x268, 0x28c },
+ { 0x15a, 0x16e, 0x185, 0x19c, 0x1b4, 0x1ce, 0x1ea, 0x207, 0x225, 0x247, 0x269, 0x28e },
+ { 0x15b, 0x16f, 0x185, 0x19d, 0x1b5, 0x1cf, 0x1eb, 0x208, 0x226, 0x248, 0x26a, 0x28f },
+ { 0x15b, 0x170, 0x186, 0x19d, 0x1b6, 0x1d0, 0x1ec, 0x209, 0x227, 0x249, 0x26b, 0x290 },
+ { 0x15c, 0x170, 0x187, 0x19e, 0x1b7, 0x1d1, 0x1ec, 0x20a, 0x228, 0x24a, 0x26d, 0x291 },
+ { 0x15d, 0x171, 0x188, 0x19f, 0x1b7, 0x1d2, 0x1ed, 0x20b, 0x229, 0x24b, 0x26e, 0x292 },
+ { 0x15d, 0x172, 0x188, 0x1a0, 0x1b8, 0x1d3, 0x1ee, 0x20c, 0x22a, 0x24c, 0x26f, 0x293 },
+ { 0x15e, 0x172, 0x189, 0x1a0, 0x1b9, 0x1d4, 0x1ef, 0x20d, 0x22b, 0x24d, 0x270, 0x295 },
+ { 0x15f, 0x173, 0x18a, 0x1a1, 0x1ba, 0x1d4, 0x1f0, 0x20e, 0x22c, 0x24e, 0x271, 0x296 },
+ { 0x15f, 0x174, 0x18a, 0x1a2, 0x1bb, 0x1d5, 0x1f1, 0x20f, 0x22d, 0x24f, 0x272, 0x297 },
+ { 0x160, 0x174, 0x18b, 0x1a3, 0x1bb, 0x1d6, 0x1f2, 0x210, 0x22e, 0x250, 0x273, 0x298 },
+ { 0x161, 0x175, 0x18c, 0x1a3, 0x1bc, 0x1d7, 0x1f3, 0x211, 0x22f, 0x251, 0x274, 0x299 },
+ { 0x161, 0x176, 0x18c, 0x1a4, 0x1bd, 0x1d8, 0x1f4, 0x212, 0x230, 0x252, 0x276, 0x29b },
+ { 0x162, 0x176, 0x18d, 0x1a5, 0x1be, 0x1d9, 0x1f5, 0x212, 0x231, 0x254, 0x277, 0x29c },
+ { 0x162, 0x177, 0x18e, 0x1a6, 0x1bf, 0x1d9, 0x1f5, 0x213, 0x232, 0x255, 0x278, 0x29d },
+ { 0x163, 0x178, 0x18f, 0x1a6, 0x1bf, 0x1da, 0x1f6, 0x214, 0x233, 0x256, 0x279, 0x29e },
+ { 0x164, 0x179, 0x18f, 0x1a7, 0x1c0, 0x1db, 0x1f7, 0x215, 0x235, 0x257, 0x27a, 0x29f },
+ { 0x164, 0x179, 0x190, 0x1a8, 0x1c1, 0x1dc, 0x1f8, 0x216, 0x236, 0x258, 0x27b, 0x2a1 },
+ { 0x165, 0x17a, 0x191, 0x1a9, 0x1c2, 0x1dd, 0x1f9, 0x217, 0x237, 0x259, 0x27c, 0x2a2 },
+ { 0x166, 0x17b, 0x192, 0x1aa, 0x1c3, 0x1de, 0x1fa, 0x218, 0x238, 0x25a, 0x27e, 0x2a3 },
+ { 0x166, 0x17b, 0x192, 0x1aa, 0x1c3, 0x1df, 0x1fb, 0x219, 0x239, 0x25b, 0x27f, 0x2a4 },
+ { 0x167, 0x17c, 0x193, 0x1ab, 0x1c4, 0x1e0, 0x1fc, 0x21a, 0x23a, 0x25c, 0x280, 0x2a6 },
+ { 0x168, 0x17d, 0x194, 0x1ac, 0x1c5, 0x1e0, 0x1fd, 0x21b, 0x23b, 0x25d, 0x281, 0x2a7 },
+ { 0x168, 0x17d, 0x194, 0x1ad, 0x1c6, 0x1e1, 0x1fe, 0x21c, 0x23c, 0x25e, 0x282, 0x2a8 },
+ { 0x169, 0x17e, 0x195, 0x1ad, 0x1c7, 0x1e2, 0x1ff, 0x21d, 0x23d, 0x260, 0x283, 0x2a9 },
+ { 0x16a, 0x17f, 0x196, 0x1ae, 0x1c8, 0x1e3, 0x1ff, 0x21e, 0x23e, 0x261, 0x284, 0x2ab },
+ { 0x16a, 0x17f, 0x197, 0x1af, 0x1c8, 0x1e4, 0x200, 0x21f, 0x23f, 0x262, 0x286, 0x2ac }
+};
+
+static inline double s_apogeeFreq(double tone)
+{
+ uint_fast32_t noteI = (uint_fast32_t)(tone);
+ int_fast32_t bendI = 0;
+ int_fast32_t outHz = 0.0;
+ double bendDec = tone - (int)tone;
+ int_fast32_t octave;
+ int_fast32_t scaleNote;
+
+ bendI = (int_fast32_t)(bendDec * 32) + 32;
+
+ noteI += bendI / 32;
+ noteI -= 1;
+
+ scaleNote = noteI % 12;
+ octave = noteI / 12;
+
+ outHz = s_apogee_freq_table[bendI % 32][scaleNote];
+
+ while(octave > 1)
+ {
+ outHz *= 2;
+ octave -= 1;
+ }
+
+ return (double)outHz;
+}
+
+
+
+
+/***************************************************************
+ * Windows 9x FM drivers frequency model *
+ ***************************************************************/
+
+//static const double s_9x_opl_samplerate = 50000.0;
+//static const double s_9x_opl_tune = 440.0;
+static const uint_fast8_t s_9x_opl_pitchfrac = 8;
+
+static const uint_fast32_t s_9x_opl_freq[12] =
+{
+ 0xAB7, 0xB5A, 0xC07, 0xCBE, 0xD80, 0xE4D, 0xF27, 0x100E, 0x1102, 0x1205, 0x1318, 0x143A
+};
+
+static const int32_t s_9x_opl_uppitch = 31;
+static const int32_t s_9x_opl_downpitch = 27;
+
+static uint_fast32_t s_9x_opl_applypitch(uint_fast32_t freq, int_fast32_t pitch)
+{
+ int32_t diff;
+
+ if(pitch > 0)
+ {
+ diff = (pitch * s_9x_opl_uppitch) >> s_9x_opl_pitchfrac;
+ freq += (diff * freq) >> 15;
+ }
+ else if (pitch < 0)
+ {
+ diff = (-pitch * s_9x_opl_downpitch) >> s_9x_opl_pitchfrac;
+ freq -= (diff * freq) >> 15;
+ }
+
+ return freq;
+}
+
+static inline double s_9xFreq(double tone)
+{
+ uint_fast32_t note = (uint_fast32_t)(tone);
+ int_fast32_t bend;
+ double bendDec = tone - (int)tone; // 0.0 ± 1.0 - one halftone
+
+ uint_fast32_t freq;
+ uint_fast32_t freqpitched;
+ uint_fast32_t octave;
+
+ uint_fast32_t bendMsb;
+ uint_fast32_t bendLsb;
+
+ bend = (int_fast32_t)(bendDec * 4096) + 8192; // convert to MIDI standard value
+
+ bendMsb = (bend >> 7) & 0x7F;
+ bendLsb = (bend & 0x7F);
+
+ bend = (bendMsb << 9) | (bendLsb << 2);
+ bend = (int16_t)(uint16_t)(bend + 0x8000);
+
+ octave = note / 12;
+ freq = s_9x_opl_freq[note % 12];
+ if(octave < 5)
+ freq >>= (5 - octave);
+ else if (octave > 5)
+ freq <<= (octave - 5);
+
+ freqpitched = s_9x_opl_applypitch(freq, bend);
+ freqpitched *= 2;
+
+ return (double)freqpitched;
+}
+
+
+
+
+/***************************************************************
+ * HMI Sound Operating System frequency model *
+ ***************************************************************/
+
+const size_t s_hmi_freqtable_size = 103;
+static uint_fast32_t s_hmi_freqtable[s_hmi_freqtable_size] =
+{
+ 0x0157, 0x016B, 0x0181, 0x0198, 0x01B0, 0x01CA, 0x01E5, 0x0202, 0x0220, 0x0241, 0x0263, 0x0287,
+ 0x0557, 0x056B, 0x0581, 0x0598, 0x05B0, 0x05CA, 0x05E5, 0x0602, 0x0620, 0x0641, 0x0663, 0x0687,
+ 0x0957, 0x096B, 0x0981, 0x0998, 0x09B0, 0x09CA, 0x09E5, 0x0A02, 0x0A20, 0x0A41, 0x0A63, 0x0A87,
+ 0x0D57, 0x0D6B, 0x0D81, 0x0D98, 0x0DB0, 0x0DCA, 0x0DE5, 0x0E02, 0x0E20, 0x0E41, 0x0E63, 0x0E87,
+ 0x1157, 0x116B, 0x1181, 0x1198, 0x11B0, 0x11CA, 0x11E5, 0x1202, 0x1220, 0x1241, 0x1263, 0x1287,
+ 0x1557, 0x156B, 0x1581, 0x1598, 0x15B0, 0x15CA, 0x15E5, 0x1602, 0x1620, 0x1641, 0x1663, 0x1687,
+ 0x1957, 0x196B, 0x1981, 0x1998, 0x19B0, 0x19CA, 0x19E5, 0x1A02, 0x1A20, 0x1A41, 0x1A63, 0x1A87,
+ 0x1D57, 0x1D6B, 0x1D81, 0x1D98, 0x1DB0, 0x1DCA, 0x1DE5, 0x1E02, 0x1E20, 0x1E41, 0x1E63, 0x1E87,
+ 0x1EAE, 0x1EB7, 0x1F02, 0x1F30, 0x1F60, 0x1F94, 0x1FCA
+};
+
+const size_t s_hmi_bendtable_size = 12;
+static uint_fast32_t s_hmi_bendtable[s_hmi_bendtable_size] =
+{
+ 0x144, 0x132, 0x121, 0x110, 0x101, 0xf8, 0xe5, 0xd8, 0xcc, 0xc1, 0xb6, 0xac
+};
+
+#define hmi_range_fix(formula, maxVal) \
+ ( \
+ (formula) < 0 ? \
+ 0 : \
+ ( \
+ (formula) >= (int32_t)maxVal ? \
+ (int32_t)maxVal : \
+ (formula) \
+ )\
+ )
+
+static uint_fast32_t s_hmi_bend_calc(uint_fast32_t bend, int_fast32_t note)
+{
+ const int_fast32_t midi_bend_range = 1;
+ uint_fast32_t bendFactor, outFreq, fmOctave, fmFreq, newFreq, idx;
+ int_fast32_t noteMod12;
+
+ note -= 12;
+// while(doNote >= 12) // ugly way to MOD 12
+// doNote -= 12;
+ noteMod12 = (note % 12);
+
+ outFreq = s_hmi_freqtable[note];
+
+ fmOctave = outFreq & 0x1c00;
+ fmFreq = outFreq & 0x3ff;
+
+ if(bend < 64)
+ {
+ bendFactor = ((63 - bend) * 1000) >> 6;
+
+ idx = hmi_range_fix(note - midi_bend_range, s_hmi_freqtable_size);
+ newFreq = outFreq - s_hmi_freqtable[idx];
+
+ if(newFreq > 719)
+ {
+ newFreq = fmFreq - s_hmi_bendtable[midi_bend_range - 1];
+ newFreq &= 0x3ff;
+ }
+
+ newFreq = (newFreq * bendFactor) / 1000;
+ outFreq -= newFreq;
+ }
+ else
+ {
+ bendFactor = ((bend - 64) * 1000) >> 6;
+
+ idx = hmi_range_fix(note + midi_bend_range, s_hmi_freqtable_size);
+ newFreq = s_hmi_freqtable[idx] - outFreq;
+
+ if(newFreq > 719)
+ {
+ idx = hmi_range_fix(11 - noteMod12, s_hmi_bendtable_size);
+ fmFreq = s_hmi_bendtable[idx];
+ outFreq = (fmOctave + 1024) | fmFreq;
+
+ idx = hmi_range_fix(note + midi_bend_range, s_hmi_freqtable_size);
+ newFreq = s_hmi_freqtable[idx] - outFreq;
+ }
+
+ newFreq = (newFreq * bendFactor) / 1000;
+ outFreq += newFreq;
+ }
+
+ return outFreq;
+}
+#undef hmi_range_fix
+
+static inline double s_hmiFreq(double tone)
+{
+ int_fast32_t note = (int_fast32_t)(tone);
+ double bendDec = tone - (int)tone; // 0.0 ± 1.0 - one halftone
+ int_fast32_t bend;
+ uint_fast32_t inFreq;
+ uint_fast32_t freq;
+ int_fast32_t octave;
+ int_fast32_t octaveOffset = 0;
+
+ bend = (int_fast32_t)(bendDec * 64.0) + 64;
+
+ while(note < 12)
+ {
+ octaveOffset--;
+ note += 12;
+ }
+ while(note > 114)
+ {
+ octaveOffset++;
+ note -= 12;
+ }
+
+ if(bend == 64)
+ inFreq = s_hmi_freqtable[note - 12];
+ else
+ inFreq = s_hmi_bend_calc(bend, note);
+
+ freq = inFreq & 0x3FF;
+ octave = (inFreq >> 10) & 0x07;
+
+ octave += octaveOffset;
+
+ while(octave > 0)
+ {
+ freq *= 2;
+ octave -= 1;
+ }
+
+ return freq;
+}
+
+
+
+
+/***************************************************************
+ * Audio Interface Library frequency model *
+ ***************************************************************/
+
+static const uint_fast16_t mo_freqtable[] = {
+ 0x02b2, 0x02b4, 0x02b7, 0x02b9, 0x02bc, 0x02be, 0x02c1, 0x02c3,
+ 0x02c6, 0x02c9, 0x02cb, 0x02ce, 0x02d0, 0x02d3, 0x02d6, 0x02d8,
+ 0x02db, 0x02dd, 0x02e0, 0x02e3, 0x02e5, 0x02e8, 0x02eb, 0x02ed,
+ 0x02f0, 0x02f3, 0x02f6, 0x02f8, 0x02fb, 0x02fe, 0x0301, 0x0303,
+ 0x0306, 0x0309, 0x030c, 0x030f, 0x0311, 0x0314, 0x0317, 0x031a,
+ 0x031d, 0x0320, 0x0323, 0x0326, 0x0329, 0x032b, 0x032e, 0x0331,
+ 0x0334, 0x0337, 0x033a, 0x033d, 0x0340, 0x0343, 0x0346, 0x0349,
+ 0x034c, 0x034f, 0x0352, 0x0356, 0x0359, 0x035c, 0x035f, 0x0362,
+ 0x0365, 0x0368, 0x036b, 0x036f, 0x0372, 0x0375, 0x0378, 0x037b,
+ 0x037f, 0x0382, 0x0385, 0x0388, 0x038c, 0x038f, 0x0392, 0x0395,
+ 0x0399, 0x039c, 0x039f, 0x03a3, 0x03a6, 0x03a9, 0x03ad, 0x03b0,
+ 0x03b4, 0x03b7, 0x03bb, 0x03be, 0x03c1, 0x03c5, 0x03c8, 0x03cc,
+ 0x03cf, 0x03d3, 0x03d7, 0x03da, 0x03de, 0x03e1, 0x03e5, 0x03e8,
+ 0x03ec, 0x03f0, 0x03f3, 0x03f7, 0x03fb, 0x03fe, 0xfe01, 0xfe03,
+ 0xfe05, 0xfe07, 0xfe08, 0xfe0a, 0xfe0c, 0xfe0e, 0xfe10, 0xfe12,
+ 0xfe14, 0xfe16, 0xfe18, 0xfe1a, 0xfe1c, 0xfe1e, 0xfe20, 0xfe21,
+ 0xfe23, 0xfe25, 0xfe27, 0xfe29, 0xfe2b, 0xfe2d, 0xfe2f, 0xfe31,
+ 0xfe34, 0xfe36, 0xfe38, 0xfe3a, 0xfe3c, 0xfe3e, 0xfe40, 0xfe42,
+ 0xfe44, 0xfe46, 0xfe48, 0xfe4a, 0xfe4c, 0xfe4f, 0xfe51, 0xfe53,
+ 0xfe55, 0xfe57, 0xfe59, 0xfe5c, 0xfe5e, 0xfe60, 0xfe62, 0xfe64,
+ 0xfe67, 0xfe69, 0xfe6b, 0xfe6d, 0xfe6f, 0xfe72, 0xfe74, 0xfe76,
+ 0xfe79, 0xfe7b, 0xfe7d, 0xfe7f, 0xfe82, 0xfe84, 0xfe86, 0xfe89,
+ 0xfe8b, 0xfe8d, 0xfe90, 0xfe92, 0xfe95, 0xfe97, 0xfe99, 0xfe9c,
+ 0xfe9e, 0xfea1, 0xfea3, 0xfea5, 0xfea8, 0xfeaa, 0xfead, 0xfeaf
+};
+
+static const uint_fast8_t mo_note_octave[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07
+};
+
+static const uint_fast8_t mo_note_halftone[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b
+};
+
+static inline double s_ailFreq(double tone)
+{
+ int_fast32_t note = (int_fast32_t)(tone);
+ double bendDec = tone - (int)tone; // 0.0 ± 1.0 - one halftone
+ int_fast32_t pitch;
+ uint_fast16_t freq;
+ int_fast32_t octave;
+ int_fast32_t octaveOffset = 0;
+ uint_fast8_t halftones;
+
+ pitch = (int_fast32_t)(bendDec * 4096) + 8192; // convert to MIDI standard value
+ pitch = ((pitch - 0x2000) / 0x20) * 2;
+
+ note -= 12;
+
+ while(note < 0)
+ {
+ octaveOffset--;
+ note += 12;
+ }
+ while(note > 95)
+ {
+ octaveOffset++;
+ note -= 12;
+ }
+
+ pitch += (((uint_fast8_t)note) << 8) + 8;
+ pitch /= 16;
+ while (pitch < 12 * 16) {
+ pitch += 12 * 16;
+ }
+ while (pitch > 96 * 16 - 1) {
+ pitch -= 12 * 16;
+ }
+
+ halftones = (mo_note_halftone[pitch >> 4] << 4) + (pitch & 0x0f);
+ freq = mo_freqtable[halftones];
+ octave = mo_note_octave[pitch >> 4];
+
+ if((freq & 0x8000) == 0)
+ {
+ if (octave > 0) {
+ octave--;
+ } else {
+ freq /= 2;
+ }
+ }
+
+ freq &= 0x3FF;
+
+ octave += octaveOffset;
+
+ while(octave > 0)
+ {
+ freq *= 2;
+ octave -= 1;
+ }
+
+ return freq;
+}
+
+
+
+
+
+
+
enum
{
MasterVolumeDefault = 127
@@ -426,10 +1008,42 @@ void OPL3::noteOff(size_t c)
writeRegI(chip, 0xB0 + g_channelsMap[cc], m_keyBlockFNumCache[c] & 0xDF);
}
-void OPL3::noteOn(size_t c1, size_t c2, double hertz) // Hertz range: 0..131071
+void OPL3::noteOn(size_t c1, size_t c2, double tone)
{
size_t chip = c1 / NUM_OF_CHANNELS, cc1 = c1 % NUM_OF_CHANNELS, cc2 = c2 % NUM_OF_CHANNELS;
uint32_t octave = 0, ftone = 0, mul_offset = 0;
+ // Hertz range: 0..131071
+ double hertz;
+
+ // Use different frequency formulas in depend on a volume model
+ switch(m_volumeScale)
+ {
+ case VOLUME_DMX:
+ case VOLUME_DMX_FIXED:
+ hertz = s_dmxFreq(tone);
+ break;
+
+ case VOLUME_APOGEE:
+ case VOLUME_APOGEE_FIXED:
+ hertz = s_apogeeFreq(tone);
+ break;
+
+ case VOLUME_9X:
+ case VOLUME_9X_GENERIC_FM:
+ hertz = s_9xFreq(tone);
+ break;
+
+ case VOLUME_HMI:
+ hertz = s_hmiFreq(tone);
+ break;
+
+ case VOLUME_AIL:
+ hertz = s_ailFreq(tone);
+ break;
+
+ default:
+ hertz = s_commonFreq(tone);
+ }
if(hertz < 0)
return;
@@ -456,7 +1070,7 @@ void OPL3::noteOn(size_t c1, size_t c2, double hertz) // Hertz range: 0..131071
{
ftone += 0x2000u; /* Key-ON [KON] */
- const bool natural_4op = (m_channelCategory[c1] == ChanCat_4op_Master);
+ const bool natural_4op = (m_channelCategory[c1] == ChanCat_4op_First);
const size_t opsCount = natural_4op ? 4 : 2;
const uint16_t op_addr[4] =
{
@@ -564,7 +1178,7 @@ void OPL3::touchNote(size_t c,
switch(m_volumeScale)
{
default:
- case Synth::VOLUME_Generic:
+ case VOLUME_Generic:
{
volume = velocity * m_masterVolume *
channelVolume * channelExpression;
@@ -592,7 +1206,7 @@ void OPL3::touchNote(size_t c,
}
break;
- case Synth::VOLUME_NATIVE:
+ case VOLUME_NATIVE:
{
volume = velocity * channelVolume * channelExpression;
// 4096766 = (127 * 127 * 127) / 2
@@ -600,8 +1214,8 @@ void OPL3::touchNote(size_t c,
}
break;
- case Synth::VOLUME_DMX:
- case Synth::VOLUME_DMX_FIXED:
+ case VOLUME_DMX:
+ case VOLUME_DMX_FIXED:
{
volume = (channelVolume * channelExpression * m_masterVolume) / 16129;
volume = (s_dmx_volume_model[volume] + 1) << 1;
@@ -609,28 +1223,28 @@ void OPL3::touchNote(size_t c,
}
break;
- case Synth::VOLUME_APOGEE:
- case Synth::VOLUME_APOGEE_FIXED:
+ case VOLUME_APOGEE:
+ case VOLUME_APOGEE_FIXED:
{
midiVolume = (channelVolume * channelExpression * m_masterVolume / 16129);
}
break;
- case Synth::VOLUME_9X:
+ case VOLUME_9X:
{
volume = (channelVolume * channelExpression * m_masterVolume) / 16129;
volume = s_w9x_sb16_volume_model[volume >> 2];
}
break;
- case Synth::VOLUME_9X_GENERIC_FM:
+ case VOLUME_9X_GENERIC_FM:
{
volume = (channelVolume * channelExpression * m_masterVolume) / 16129;
volume = s_w9x_generic_fm_volume_model[volume >> 2];
}
break;
- case Synth::VOLUME_AIL:
+ case VOLUME_AIL:
{
midiVolume = (channelVolume * channelExpression) * 2;
midiVolume >>= 8;
@@ -650,7 +1264,7 @@ void OPL3::touchNote(size_t c,
}
break;
- case Synth::VOLUME_HMI:
+ case VOLUME_HMI:
{
volume = (channelVolume * channelExpression * m_masterVolume) / 16129;
volume = (((volume * 128) / 127) * velocity) >> 7;
@@ -671,12 +1285,12 @@ void OPL3::touchNote(size_t c,
{
mode = adli.feedconn & 1; // 2-op FM or 2-op AM
}
- else if(m_channelCategory[c] == ChanCat_4op_Master ||
- m_channelCategory[c] == ChanCat_4op_Slave)
+ else if(m_channelCategory[c] == ChanCat_4op_First ||
+ m_channelCategory[c] == ChanCat_4op_Second)
{
const adldata *i0, *i1;
- if(m_channelCategory[c] == ChanCat_4op_Master)
+ if(m_channelCategory[c] == ChanCat_4op_First)
{
i0 = &adli;
i1 = &m_insCache[c + 3];
@@ -904,7 +1518,7 @@ void OPL3::updateChannelCategories()
for(size_t b = 0; b < NUM_OF_CHANNELS; ++b)
{
m_channelCategory[a * NUM_OF_CHANNELS + b] =
- (b >= OPL3_CHANNELS_RHYTHM_BASE) ? ChanCat_Rhythm_Slave : ChanCat_Regular;
+ (b >= OPL3_CHANNELS_RHYTHM_BASE) ? ChanCat_Rhythm_Secondary : ChanCat_Regular;
}
}
}
@@ -916,7 +1530,7 @@ void OPL3::updateChannelCategories()
{
m_channelCategory[a * NUM_OF_CHANNELS + b] =
(b >= OPL3_CHANNELS_RHYTHM_BASE) ? static_cast<ChanCat>(ChanCat_Rhythm_Bass + (b - OPL3_CHANNELS_RHYTHM_BASE)) :
- (b >= 6 && b < 9) ? ChanCat_Rhythm_Slave : ChanCat_Regular;
+ (b >= 6 && b < 9) ? ChanCat_Rhythm_Secondary : ChanCat_Regular;
}
}
}
@@ -924,8 +1538,8 @@ void OPL3::updateChannelCategories()
uint32_t nextfour = 0;
for(uint32_t a = 0; a < fours; ++a)
{
- m_channelCategory[nextfour] = ChanCat_4op_Master;
- m_channelCategory[nextfour + 3] = ChanCat_4op_Slave;
+ m_channelCategory[nextfour] = ChanCat_4op_First;
+ m_channelCategory[nextfour + 3] = ChanCat_4op_Second;
switch(a % 6)
{
@@ -964,9 +1578,9 @@ void OPL3::updateChannelCategories()
Channel 0: 00 00 03 03 06 08 09 0B
Channel 1: 01 01 04 04 07 09 10 0C
Channel 2: 02 02 05 05 08 0A 11 0D
- Channel 3: CHANNEL 0 SLAVE
- Channel 4: CHANNEL 1 SLAVE
- Channel 5: CHANNEL 2 SLAVE
+ Channel 3: CHANNEL 0 SECONDARY
+ Channel 4: CHANNEL 1 SECONDARY
+ Channel 5: CHANNEL 2 SECONDARY
Channel 6: 12 10 15 13
Channel 7: 13 11 16 14
Channel 8: 14 12 17 15
@@ -1100,7 +1714,7 @@ void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
for(size_t b = 0; b < OPL3_CHANNELS_RHYTHM_BASE; ++b)
m_channelCategory[p++] = ChanCat_Regular;
for(size_t b = 0; b < NUM_OF_RM_CHANNELS; ++b)
- m_channelCategory[p++] = ChanCat_Rhythm_Slave;
+ m_channelCategory[p++] = ChanCat_Rhythm_Secondary;
}
static const uint16_t data[] =