aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi_opl3.cpp
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2020-08-21 13:31:53 +0300
committerWohlstand <admin@wohlnet.ru>2020-08-21 13:31:53 +0300
commit0f6e197d1f2243e1fb9415e1ea2209c39b972650 (patch)
tree5dc9e14cd51e914b200737815961e4b0d76079d9 /src/adlmidi_opl3.cpp
parentb073a8029a46b646f6d593a56ffbe449dd582e39 (diff)
downloadlibADLMIDI-0f6e197d1f2243e1fb9415e1ea2209c39b972650.tar.gz
libADLMIDI-0f6e197d1f2243e1fb9415e1ea2209c39b972650.tar.bz2
libADLMIDI-0f6e197d1f2243e1fb9415e1ea2209c39b972650.zip
More accurate DMX volume model
Also, move entire code of volume model from MIDIPlay into OPL3
Diffstat (limited to 'src/adlmidi_opl3.cpp')
-rw-r--r--src/adlmidi_opl3.cpp201
1 files changed, 168 insertions, 33 deletions
diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp
index 7ba4a13..887dd09 100644
--- a/src/adlmidi_opl3.cpp
+++ b/src/adlmidi_opl3.cpp
@@ -191,6 +191,41 @@ static const uint16_t g_channelsMapPan[NUM_OF_CHANNELS] =
Ports: ???
*/
+// Mapping from MIDI volume level to OPL level value.
+
+static const uint_fast32_t DMX_volume_mapping_table[128] =
+{
+ 0, 1, 3, 5, 6, 8, 10, 11,
+ 13, 14, 16, 17, 19, 20, 22, 23,
+ 25, 26, 27, 29, 30, 32, 33, 34,
+ 36, 37, 39, 41, 43, 45, 47, 49,
+ 50, 52, 54, 55, 57, 59, 60, 61,
+ 63, 64, 66, 67, 68, 69, 71, 72,
+ 73, 74, 75, 76, 77, 79, 80, 81,
+ 82, 83, 84, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 92, 93, 94, 95,
+ 96, 96, 97, 98, 99, 99, 100, 101,
+ 101, 102, 103, 103, 104, 105, 105, 106,
+ 107, 107, 108, 109, 109, 110, 110, 111,
+ 112, 112, 113, 113, 114, 114, 115, 115,
+ 116, 117, 117, 118, 118, 119, 119, 120,
+ 120, 121, 121, 122, 122, 123, 123, 123,
+ 124, 124, 125, 125, 126, 126, 127, 127,
+};
+
+static const uint_fast32_t W9X_volume_mapping_table[32] =
+{
+ 63, 63, 40, 36, 32, 28, 23, 21,
+ 19, 17, 15, 14, 13, 12, 11, 10,
+ 9, 8, 7, 6, 5, 5, 4, 4,
+ 3, 3, 2, 2, 1, 1, 0, 0
+};
+
+enum
+{
+ MasterVolumeDefault = 127
+};
+
enum
{
OPL_PANNING_LEFT = 0x10,
@@ -198,6 +233,8 @@ enum
OPL_PANNING_BOTH = 0x30
};
+
+
static adlinsdata2 makeEmptyInstrument()
{
adlinsdata2 ins;
@@ -215,6 +252,7 @@ OPL3::OPL3() :
m_deepVibratoMode(false),
m_rhythmMode(false),
m_softPanning(false),
+ m_masterVolume(MasterVolumeDefault),
m_musicMode(MODE_MIDI),
m_volumeScale(VOLUME_Generic)
{
@@ -418,11 +456,12 @@ void OPL3::noteOn(size_t c1, size_t c2, double hertz) // Hertz range: 0..131071
}
}
-void OPL3::touchNote(size_t c, uint8_t volume, uint8_t brightness)
+void OPL3::touchNote(size_t c,
+ uint_fast32_t velocity,
+ uint_fast32_t channelVolume,
+ uint_fast32_t channelExpression,
+ uint8_t brightness)
{
- if(volume > 63)
- volume = 63;
-
size_t chip = c / NUM_OF_CHANNELS, cc = c % NUM_OF_CHANNELS;
const adldata &adli = m_insCache[c];
size_t cmf_offset = ((m_musicMode == MODE_CMF) && cc >= OPL3_CHANNELS_RHYTHM_BASE) ? 10 : 0;
@@ -431,6 +470,94 @@ void OPL3::touchNote(size_t c, uint8_t volume, uint8_t brightness)
uint8_t x = adli.modulator_40, y = adli.carrier_40;
uint32_t mode = 1; // 2-op AM
+ uint_fast32_t kslMod = x & 0xC0;
+ uint_fast32_t kslCar = y & 0xC0;
+ uint_fast32_t tlMod = x & 63;
+ uint_fast32_t tlCar = y & 63;
+
+ uint_fast32_t modulator;
+ uint_fast32_t carrier;
+
+ uint_fast32_t volume;
+
+ bool do_modulator;
+ bool do_carrier;
+
+ static const bool do_ops[10][2] =
+ {
+ { false, true }, /* 2 op FM */
+ { true, true }, /* 2 op AM */
+ { false, false }, /* 4 op FM-FM ops 1&2 */
+ { true, false }, /* 4 op AM-FM ops 1&2 */
+ { false, true }, /* 4 op FM-AM ops 1&2 */
+ { true, false }, /* 4 op AM-AM ops 1&2 */
+ { false, true }, /* 4 op FM-FM ops 3&4 */
+ { false, true }, /* 4 op AM-FM ops 3&4 */
+ { false, true }, /* 4 op FM-AM ops 3&4 */
+ { true, true } /* 4 op AM-AM ops 3&4 */
+ };
+
+
+ switch(m_volumeScale)
+ {
+ default:
+ case Synth::VOLUME_Generic:
+ {
+ volume = velocity * m_masterVolume * channelVolume * channelExpression;
+
+ /* If the channel has arpeggio, the effective volume of
+ * *this* instrument is actually lower due to timesharing.
+ * To compensate, add extra volume that corresponds to the
+ * time this note is *not* heard.
+ * Empirical tests however show that a full equal-proportion
+ * increment sounds wrong. Therefore, using the square root.
+ */
+ //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() ));
+
+ // The formula below: SOLVE(V=127^4 * 2^( (A-63.49999) / 8), A)
+ volume = volume > (8725 * 127) ? static_cast<uint_fast32_t>(std::log(static_cast<double>(volume)) * 11.541560327111707 - 1.601379199767093e+02) : 0;
+ // The incorrect formula below: SOLVE(V=127^4 * (2^(A/63)-1), A)
+ //opl.Touch_Real(c, volume>(11210*127) ? 91.61112 * std::log((4.8819E-7/127)*volume + 1.0)+0.5 : 0);
+ }
+ break;
+
+ case Synth::VOLUME_NATIVE:
+ {
+ volume = velocity * channelVolume * channelExpression;
+ // volume = volume * m_masterVolume / (127 * 127 * 127) / 2;
+ volume = (volume * m_masterVolume) / 4096766;
+ }
+ break;
+
+ case Synth::VOLUME_DMX:
+ {
+ volume = (channelVolume * channelExpression * m_masterVolume) / 16129;
+ volume = (DMX_volume_mapping_table[volume] + 1) << 1;
+ volume = (DMX_volume_mapping_table[(velocity < 128) ? velocity : 127] * volume) >> 9;
+ }
+ break;
+
+ case Synth::VOLUME_APOGEE:
+ {
+ volume = (channelVolume * channelExpression * m_masterVolume / 16129);
+ volume = ((64 * (velocity + 0x80)) * volume) >> 15;
+ //volume = ((63 * (velocity + 0x80)) * Ch[MidCh].volume) >> 15;
+ }
+ break;
+
+ case Synth::VOLUME_9X:
+ {
+ //volume = 63 - W9X_volume_mapping_table[(((velocity * Ch[MidCh].volume /** Ch[MidCh].expression*/) * m_masterVolume / 16129 /*2048383*/) >> 2)];
+ volume = 63 - W9X_volume_mapping_table[((velocity * channelVolume * channelExpression * m_masterVolume / 2048383) >> 2)];
+ //volume = W9X_volume_mapping_table[vol >> 2] + volume;
+ }
+ break;
+ }
+
+ if(volume > 63)
+ volume = 63;
+
+
if(m_channelCategory[c] == ChanCat_Regular ||
m_channelCategory[c] == ChanCat_Rhythm_Bass)
{
@@ -457,50 +584,58 @@ void OPL3::touchNote(size_t c, uint8_t volume, uint8_t brightness)
mode += (i0->feedconn & 1) + (i1->feedconn & 1) * 2;
}
- static const bool do_ops[10][2] =
- {
- { false, true }, /* 2 op FM */
- { true, true }, /* 2 op AM */
- { false, false }, /* 4 op FM-FM ops 1&2 */
- { true, false }, /* 4 op AM-FM ops 1&2 */
- { false, true }, /* 4 op FM-AM ops 1&2 */
- { true, false }, /* 4 op AM-AM ops 1&2 */
- { false, true }, /* 4 op FM-FM ops 3&4 */
- { false, true }, /* 4 op AM-FM ops 3&4 */
- { false, true }, /* 4 op FM-AM ops 3&4 */
- { true, true } /* 4 op AM-AM ops 3&4 */
- };
if(m_musicMode == MODE_RSXX)
{
- if(o1 != 0xFFF)
- writeRegI(chip, 0x40 + o1, x);
- if(o2 != 0xFFF)
- writeRegI(chip, 0x40 + o2, y - volume / 2);
+ tlCar -= volume / 2;
+ }
+ else if(m_volumeScale == Synth::VOLUME_DMX && mode <= 1)
+ {
+ do_modulator = do_ops[mode][ 0 ] || m_scaleModulators;
+
+ tlCar = (63 - volume);
+
+ if(do_modulator)
+ {
+ if(tlMod < tlCar)
+ tlMod = tlCar;
+ }
+
+ if(brightness != 127)
+ {
+ brightness = static_cast<uint8_t>(::round(127.0 * ::sqrt((static_cast<double>(brightness)) * (1.0 / 127.0))) / 2.0);
+ if(!do_modulator)
+ tlMod = 63 - brightness + (brightness * tlMod) / 63;
+ }
}
else
{
- bool do_modulator = do_ops[ mode ][ 0 ] || m_scaleModulators;
- bool do_carrier = do_ops[ mode ][ 1 ] || m_scaleModulators;
+ do_modulator = do_ops[ mode ][ 0 ] || m_scaleModulators;
+ do_carrier = do_ops[ mode ][ 1 ] || m_scaleModulators;
- uint32_t modulator = do_modulator ? (x | 63) - volume + volume * (x & 63) / 63 : x;
- uint32_t carrier = do_carrier ? (y | 63) - volume + volume * (y & 63) / 63 : y;
+ if(do_modulator)
+ tlMod = 63 - volume + (volume * tlMod) / 63;
+ if(do_carrier)
+ tlCar = 63 - volume + (volume * tlCar) / 63;
if(brightness != 127)
{
brightness = static_cast<uint8_t>(::round(127.0 * ::sqrt((static_cast<double>(brightness)) * (1.0 / 127.0))) / 2.0);
if(!do_modulator)
- modulator = (modulator | 63) - brightness + brightness * (modulator & 63) / 63;
+ tlMod = 63 - brightness + (brightness * tlMod) / 63;
if(!do_carrier)
- carrier = (carrier | 63) - brightness + brightness * (carrier & 63) / 63;
+ tlCar = 63 - brightness + (brightness * tlCar) / 63;
}
-
- if(o1 != 0xFFF)
- writeRegI(chip, 0x40 + o1, modulator);
- if(o2 != 0xFFF)
- writeRegI(chip, 0x40 + o2, carrier);
}
+ modulator = (kslMod & 0xC0) | (tlMod & 63);
+ carrier = (kslCar & 0xC0) | (tlCar & 63);
+
+ if(o1 != 0xFFF)
+ writeRegI(chip, 0x40 + o1, modulator);
+ if(o2 != 0xFFF)
+ writeRegI(chip, 0x40 + o2, carrier);
+
// Correct formula (ST3, AdPlug):
// 63-((63-(instrvol))/63)*chanvol
// Reduces to (tested identical):
@@ -572,7 +707,7 @@ void OPL3::silenceAll() // Silence all OPL channels.
for(size_t c = 0; c < m_numChannels; ++c)
{
noteOff(c);
- touchNote(c, 0);
+ touchNote(c, 0, 0, 0);
}
}