diff options
-rw-r--r-- | src/adlmidi_cvt.hpp | 11 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 111 | ||||
-rw-r--r-- | src/adlmidi_midiplay.hpp | 3 | ||||
-rw-r--r-- | src/adlmidi_opl3.cpp | 201 | ||||
-rw-r--r-- | src/adlmidi_opl3.hpp | 10 | ||||
-rw-r--r-- | src/cvt_mus2mid.hpp | 6 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_op2.h | 7 | ||||
-rw-r--r-- | utils/gen_adldata/file_formats/load_wopl.h | 8 |
8 files changed, 201 insertions, 156 deletions
diff --git a/src/adlmidi_cvt.hpp b/src/adlmidi_cvt.hpp index cb16eb0..3b2e07d 100644 --- a/src/adlmidi_cvt.hpp +++ b/src/adlmidi_cvt.hpp @@ -29,15 +29,12 @@ template <class WOPLI> static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in) { ins.voice2_fine_tune = 0.0; - int8_t voice2_fine_tune = in.second_voice_detune; + int voice2_fine_tune = in.second_voice_detune; + if(voice2_fine_tune != 0) { - if(voice2_fine_tune == 1) - ins.voice2_fine_tune = 0.000025; - else if(voice2_fine_tune == -1) - ins.voice2_fine_tune = -0.000025; - else - ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0); + // Simulate behavior of DMX second voice detune + ins.voice2_fine_tune = (double)(((voice2_fine_tune + 128) >> 1) - 64) / 32.0; } ins.midi_velocity_offset = in.midi_velocity_offset; diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 697a264..559ed69 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -29,38 +29,6 @@ // Minimum life time of percussion notes static const double drum_note_min_time = 0.03; - -// 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 -}; - - //static const char MIDIsymbols[256+1] = //"PPPPPPhcckmvmxbd" // Ins 0-15 //"oooooahoGGGGGGGG" // Ins 16-31 @@ -110,7 +78,6 @@ void MIDIplay::AdlChannel::addAge(int64_t us) MIDIplay::MIDIplay(unsigned long sampleRate): m_cmfPercussionMode(false), - m_masterVolume(MasterVolumeDefault), m_sysExDeviceId(0), m_synthMode(Mode_XG), m_arpeggioCounter(0) @@ -224,7 +191,8 @@ void MIDIplay::partialReset() void MIDIplay::resetMIDI() { - m_masterVolume = MasterVolumeDefault; + Synth &synth = *m_synth; + synth.m_masterVolume = MasterVolumeDefault; m_sysExDeviceId = 0; m_synthMode = Mode_XG; m_arpeggioCounter = 0; @@ -299,7 +267,7 @@ void MIDIplay::realTime_ResetState() noteUpdateAll(uint16_t(ch), Upd_All); noteUpdateAll(uint16_t(ch), Upd_Off); } - m_masterVolume = MasterVolumeDefault; + synth.m_masterVolume = MasterVolumeDefault; } bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) @@ -969,7 +937,8 @@ bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data unsigned volume = (((unsigned)data[0] & 0x7F)) | (((unsigned)data[1] & 0x7F) << 7); - m_masterVolume = static_cast<uint8_t>(volume >> 7); + if(m_synth.get()) + m_synth->m_masterVolume = static_cast<uint8_t>(volume >> 7); for(size_t ch = 0; ch < m_midiChannels.size(); ch++) noteUpdateAll(uint16_t(ch), Upd_Volume); return true; @@ -1245,7 +1214,7 @@ void MIDIplay::noteUpdate(size_t midCh, synth.noteOff(c); if(props_mask & Upd_Mute) // Mute the note { - synth.touchNote(c, 0); + synth.touchNote(c, 0, 0, 0); m_chipChannels[c].koff_time_until_neglible_us = 0; } else @@ -1275,9 +1244,9 @@ void MIDIplay::noteUpdate(size_t midCh, if(props_mask & Upd_Volume) { - uint_fast32_t volume; - bool is_percussion = (midCh == 9) || m_midiChannels[midCh].is_xg_percussion; - uint_fast32_t brightness = is_percussion ? 127 : m_midiChannels[midCh].brightness; + const MIDIchannel &ch = m_midiChannels[midCh]; + bool is_percussion = (midCh == 9) || ch.is_xg_percussion; + uint_fast32_t brightness = is_percussion ? 127 : ch.brightness; if(!m_setup.fullRangeBrightnessCC74) { @@ -1288,63 +1257,11 @@ void MIDIplay::noteUpdate(size_t midCh, brightness *= 2; } - switch(synth.m_volumeScale) - { - default: - case Synth::VOLUME_Generic: - { - volume = vol * m_masterVolume * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; - - /* 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 = vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; - // volume = volume * m_masterVolume / (127 * 127 * 127) / 2; - volume = (volume * m_masterVolume) / 4096766; - } - break; - - case Synth::VOLUME_DMX: - { - volume = (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume) / 16129; - volume = (DMX_volume_mapping_table[volume] + 1) << 1; - volume = (DMX_volume_mapping_table[(vol < 128) ? vol : 127] * volume) >> 9; - } - break; - - case Synth::VOLUME_APOGEE: - { - volume = (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129); - volume = ((64 * (vol + 0x80)) * volume) >> 15; - //volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15; - } - break; - - case Synth::VOLUME_9X: - { - //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * m_masterVolume / 16129 /*2048383*/) >> 2)]; - volume = 63 - W9X_volume_mapping_table[((vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 2048383) >> 2)]; - //volume = W9X_volume_mapping_table[vol >> 2] + volume; - } - break; - } - - synth.touchNote(c, static_cast<uint8_t>(volume), static_cast<uint8_t>(brightness)); + synth.touchNote(c, + vol, + ch.volume, + ch.expression, + static_cast<uint8_t>(brightness)); /* DEBUG ONLY!!! static uint32_t max = 0; diff --git a/src/adlmidi_midiplay.hpp b/src/adlmidi_midiplay.hpp index 9cf35b1..f7732fe 100644 --- a/src/adlmidi_midiplay.hpp +++ b/src/adlmidi_midiplay.hpp @@ -538,9 +538,6 @@ public: //! CMF Rhythm mode bool m_cmfPercussionMode; - //! Master volume, controlled via SysEx - uint8_t m_masterVolume; - //! SysEx device ID uint8_t m_sysExDeviceId; diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp index 09e1a19..7938710 100644 --- a/src/adlmidi_opl3.cpp +++ b/src/adlmidi_opl3.cpp @@ -195,6 +195,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, @@ -202,6 +237,8 @@ enum OPL_PANNING_BOTH = 0x30 }; + + static adlinsdata2 makeEmptyInstrument() { adlinsdata2 ins; @@ -219,6 +256,7 @@ OPL3::OPL3() : m_deepVibratoMode(false), m_rhythmMode(false), m_softPanning(false), + m_masterVolume(MasterVolumeDefault), m_musicMode(MODE_MIDI), m_volumeScale(VOLUME_Generic) { @@ -486,11 +524,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; @@ -499,6 +538,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) { @@ -525,50 +652,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): @@ -640,7 +775,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); } } diff --git a/src/adlmidi_opl3.hpp b/src/adlmidi_opl3.hpp index b97b5d3..a12069e 100644 --- a/src/adlmidi_opl3.hpp +++ b/src/adlmidi_opl3.hpp @@ -110,6 +110,8 @@ public: bool m_runAtPcmRate; //! Enable soft panning bool m_softPanning; + //! Master volume, controlled via SysEx (0...127) + uint8_t m_masterVolume; //! Just a padding. Reserved. char _padding2[3]; @@ -249,10 +251,14 @@ public: /** * @brief Change setup of instrument in specified chip channel * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) - * @param volume Volume level (from 0 to 63) + * @param volume Volume level (from 0 to 127) * @param brightness CC74 Brightness level (from 0 to 127) */ - void touchNote(size_t c, uint8_t volume, uint8_t brightness = 127); + void touchNote(size_t c, + uint_fast32_t velocity, + uint_fast32_t channelVolume = 127, + uint_fast32_t channelExpression = 127, + uint8_t brightness = 127); /** * @brief Set the instrument into specified chip channel diff --git a/src/cvt_mus2mid.hpp b/src/cvt_mus2mid.hpp index 4b07b0d..fcd586d 100644 --- a/src/cvt_mus2mid.hpp +++ b/src/cvt_mus2mid.hpp @@ -307,11 +307,11 @@ static int Convert_mus2midi(uint8_t *in, uint32_t insize, mus2mid_write1(&ctx, (MUS_TEMPO & 0x0000ff00) >> 8); mus2mid_write1(&ctx, (MUS_TEMPO & 0x00ff0000) >> 16); - /* Percussions channel starts out at full volume */ + /* Percussions channel starts out at volume 100 */ mus2mid_write1(&ctx, 0x00); mus2mid_write1(&ctx, 0xB9); mus2mid_write1(&ctx, 0x07); - mus2mid_write1(&ctx, 127); + mus2mid_write1(&ctx, 100); /* get current position in source, and end of position */ cur = in + header.scoreStart; @@ -340,7 +340,7 @@ static int Convert_mus2midi(uint8_t *in, uint32_t insize, if (channelMap[channel] < 0) { *out_local++ = 0xB0 + currentChannel; *out_local++ = 0x07; - *out_local++ = 127; + *out_local++ = 100; *out_local++ = 0x00; channelMap[channel] = currentChannel++; if (currentChannel == 9) diff --git a/utils/gen_adldata/file_formats/load_op2.h b/utils/gen_adldata/file_formats/load_op2.h index 70b23de..494655d 100644 --- a/utils/gen_adldata/file_formats/load_op2.h +++ b/utils/gen_adldata/file_formats/load_op2.h @@ -145,11 +145,8 @@ bool BankFormats::LoadDoom(BanksDump &db, const char *fn, unsigned bank, const s else // Double instrument { tmp2.pseudo4op = true; - tmp2.voice2_fine_tune = (((double)ins.finetune - 128.0) * 15.625) / 1000.0; - if(ins.finetune == 129) - tmp2.voice2_fine_tune = 0.000025; - else if(ins.finetune == 127) - tmp2.voice2_fine_tune = -0.000025; + // Simulate behavior of DMX second voice detune + tmp2.voice2_fine_tune = (double)((ins.finetune >> 1) - 64) / 32.0; //printf("/*DOOM FINE TUNE (flags %000X instrument is %d) IS %d -> %lf*/\n", ins.flags, a, ins.finetune, tmp2.fine_tune); // size_t resno = InsertIns(tmp[0], tmp[1], tmp2, std::string(1, '\377') + name, name2); // SetBank(bank, (unsigned int)gmno, resno); diff --git a/utils/gen_adldata/file_formats/load_wopl.h b/utils/gen_adldata/file_formats/load_wopl.h index 265c6bf..a60cbc4 100644 --- a/utils/gen_adldata/file_formats/load_wopl.h +++ b/utils/gen_adldata/file_formats/load_wopl.h @@ -223,12 +223,8 @@ bool BankFormats::LoadWopl(BanksDump &db, const char *fn, unsigned bank, const s int8_t fine_tune = (int8_t)data[offset + 37]; if(fine_tune != 0) { - if(fine_tune == 1) - tmp2.voice2_fine_tune = 0.000025; - else if(fine_tune == -1) - tmp2.voice2_fine_tune = -0.000025; - else - tmp2.voice2_fine_tune = ((fine_tune * 15.625) / 1000.0); + // Simulate behavior of DMX second voice detune + tmp2.voice2_fine_tune = (double)((((int)fine_tune + 128) >> 1) - 64) / 32.0; } uint32_t gmno = is_percussion ? i + 128 : i; |