diff options
author | Filippos Karapetis | 2014-07-07 22:33:12 +0300 |
---|---|---|
committer | Filippos Karapetis | 2014-07-08 00:02:18 +0300 |
commit | 8c5f67568f1a320134d73c2efe41ff57dfdc1fdd (patch) | |
tree | 0e938c157af6a7b285e14e5f1cd7108f64fe90bb | |
parent | 10c7986a36d0fadfc2373c8b8728855dcf8a4cad (diff) | |
download | scummvm-rg350-8c5f67568f1a320134d73c2efe41ff57dfdc1fdd.tar.gz scummvm-rg350-8c5f67568f1a320134d73c2efe41ff57dfdc1fdd.tar.bz2 scummvm-rg350-8c5f67568f1a320134d73c2efe41ff57dfdc1fdd.zip |
MT32: Update to munt 1.4.0
This syncs with munt commit 175446af43
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | audio/softsynth/mt32/BReverbModel.cpp | 312 | ||||
-rw-r--r-- | audio/softsynth/mt32/BReverbModel.h | 6 | ||||
-rw-r--r-- | audio/softsynth/mt32/ROMInfo.cpp | 24 | ||||
-rw-r--r-- | audio/softsynth/mt32/ROMInfo.h | 13 | ||||
-rw-r--r-- | audio/softsynth/mt32/Synth.cpp | 338 | ||||
-rw-r--r-- | audio/softsynth/mt32/Synth.h | 42 |
7 files changed, 470 insertions, 267 deletions
@@ -12,7 +12,7 @@ For a more comprehensive changelog of the latest experimental code, see: - Added support for Voyeur. General: - - Updated Munt MT-32 emulation code to version 1.3.0. + - Updated Munt MT-32 emulation code to version 1.4.0. - Switched from our custom JPEG and PNG decoders to libjpeg(-turbo) and libpng, which are faster and can handle more images. (NOTE: The change to libpng was done in version 1.6.0, but it was not diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp index 38cc592c95..37b3e9670d 100644 --- a/audio/softsynth/mt32/BReverbModel.cpp +++ b/audio/softsynth/mt32/BReverbModel.cpp @@ -34,70 +34,143 @@ static const Bit32u PROCESS_DELAY = 1; static const Bit32u MODE_3_ADDITIONAL_DELAY = 1; static const Bit32u MODE_3_FEEDBACK_DELAY = 1; -// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. +// Default reverb settings for "new" reverb model implemented in CM-32L / LAPC-I. // Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). +const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode) { + static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3; + static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; + static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be processed via a hacked comb. + static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; + static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; + static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; + static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; + static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; + static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; + static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit32u MODE_0_LPF_AMP = 0x60; + + static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; + static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; + static const Bit32u MODE_1_NUMBER_OF_COMBS = 4; // Same as for mode 0 above + static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; + static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; + static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; + static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; + static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; + static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; + static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit32u MODE_1_LPF_AMP = 0x60; + + static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; + static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; + static const Bit32u MODE_2_NUMBER_OF_COMBS = 4; // Same as for mode 0 above + static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; + static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; + static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; + static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; + static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; + static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; + static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit32u MODE_2_LPF_AMP = 0x80; + + static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; + static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; + static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; + static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; + static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; + static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; + static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; + static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50}; + static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + + static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; + static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; + static const BReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_NUMBER_OF_ALLPASSES, MODE_2_ALLPASSES, MODE_2_NUMBER_OF_COMBS, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_DRY_AMP, MODE_2_WET_AMP, MODE_2_LPF_AMP}; + static const BReverbSettings REVERB_MODE_3_SETTINGS = {MODE_3_NUMBER_OF_ALLPASSES, NULL, MODE_3_NUMBER_OF_COMBS, MODE_3_DELAY, MODE_3_OUTL, MODE_3_OUTR, MODE_3_COMB_FACTOR, MODE_3_COMB_FEEDBACK, MODE_3_DRY_AMP, MODE_3_WET_AMP, 0}; + + static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_3_SETTINGS}; + + return *REVERB_SETTINGS[mode]; +} -static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3; -static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; -static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be processed via a hacked comb. -static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; -static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; -static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; -static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; -static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; -static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; -static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; -static const Bit32u MODE_0_LPF_AMP = 0x60; - -static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; -static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; -static const Bit32u MODE_1_NUMBER_OF_COMBS = 4; // Same as for mode 0 above -static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; -static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; -static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; -static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; -static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; -static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; -static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; -static const Bit32u MODE_1_LPF_AMP = 0x60; - -static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; -static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; -static const Bit32u MODE_2_NUMBER_OF_COMBS = 4; // Same as for mode 0 above -static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; -static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; -static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; -static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; -static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, - 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, - 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; -static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; -static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; -static const Bit32u MODE_2_LPF_AMP = 0x80; - -static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; -static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; -static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; -static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; -static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; -static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; -static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; -static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50}; -static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; - -static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; -static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; -static const BReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_NUMBER_OF_ALLPASSES, MODE_2_ALLPASSES, MODE_2_NUMBER_OF_COMBS, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_DRY_AMP, MODE_2_WET_AMP, MODE_2_LPF_AMP}; -static const BReverbSettings REVERB_MODE_3_SETTINGS = {MODE_3_NUMBER_OF_ALLPASSES, NULL, MODE_3_NUMBER_OF_COMBS, MODE_3_DELAY, MODE_3_OUTL, MODE_3_OUTR, MODE_3_COMB_FACTOR, MODE_3_COMB_FEEDBACK, MODE_3_DRY_AMP, MODE_3_WET_AMP, 0}; - -static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_3_SETTINGS}; +// Default reverb settings for "old" reverb model implemented in MT-32. +// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). +const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) { + static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3; + static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; + static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Same as above in the new model implementation + static const Bit32u MODE_0_COMBS[] = {575 + PROCESS_DELAY, 2040, 2752, 3629}; + static const Bit32u MODE_0_OUTL[] = {2040, 687, 1814}; + static const Bit32u MODE_0_OUTR[] = {1019, 2072, 1}; + static const Bit32u MODE_0_COMB_FACTOR[] = {0xB0, 0x60, 0x60, 0x60}; + static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; + static const Bit32u MODE_0_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit32u MODE_0_LPF_AMP = 0x80; + + static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; + static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; + static const Bit32u MODE_1_NUMBER_OF_COMBS = 4; // Same as above in the new model implementation + static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; + static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; + static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; + static const Bit32u MODE_1_COMB_FACTOR[] = {0x90, 0x60, 0x60, 0x60}; + static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; + static const Bit32u MODE_1_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit32u MODE_1_LPF_AMP = 0x80; + + static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; + static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; + static const Bit32u MODE_2_NUMBER_OF_COMBS = 4; // Same as above in the new model implementation + static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; + static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; + static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; + static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x60, 0x60, 0x60}; + static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; + static const Bit32u MODE_2_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit32u MODE_2_LPF_AMP = 0x80; + + static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; + static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; + static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; + static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; + static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; + static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; + static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; + static const Bit32u MODE_3_DRY_AMP[] = {0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x10, 0x20, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10}; + static const Bit32u MODE_3_WET_AMP[] = {0x08, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + + static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; + static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; + static const BReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_NUMBER_OF_ALLPASSES, MODE_2_ALLPASSES, MODE_2_NUMBER_OF_COMBS, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_DRY_AMP, MODE_2_WET_AMP, MODE_2_LPF_AMP}; + static const BReverbSettings REVERB_MODE_3_SETTINGS = {MODE_3_NUMBER_OF_ALLPASSES, NULL, MODE_3_NUMBER_OF_COMBS, MODE_3_DELAY, MODE_3_OUTL, MODE_3_OUTR, MODE_3_COMB_FACTOR, MODE_3_COMB_FEEDBACK, MODE_3_DRY_AMP, MODE_3_WET_AMP, 0}; + + static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_3_SETTINGS}; + + return *REVERB_SETTINGS[mode]; +} // This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines). // Also LA32 is suspected to use the similar one to perform PCM interpolation and ring modulation. @@ -153,14 +226,7 @@ bool RingBuffer::isEmpty() const { } void RingBuffer::mute() { -#if MT32EMU_USE_FLOAT_SAMPLES - Sample *buf = buffer; - for (Bit32u i = 0; i < size; i++) { - *buf++ = 0; - } -#else - memset(buffer, 0, size * sizeof(Sample)); -#endif + Synth::muteSampleBuffer(buffer, size); } AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} @@ -195,10 +261,10 @@ void CombFilter::process(const Sample in) { const Sample last = buffer[index]; // prepare input + feedback - const Sample filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */); + const Sample filterIn = in + weirdMul(next(), feedbackFactor, 0xF0); // store input + feedback processed by a low-pass filter - buffer[index] = weirdMul(last, filterFactor, 0x40) - filterIn; + buffer[index] = weirdMul(last, filterFactor, 0xC0) - filterIn; } Sample CombFilter::getOutputAt(const Bit32u outIndex) const { @@ -256,8 +322,10 @@ void TapDelayCombFilter::setOutputPositions(const Bit32u useOutL, const Bit32u u outR = useOutR; } -BReverbModel::BReverbModel(const ReverbMode mode) - : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]), tapDelayMode(mode == REVERB_MODE_TAP_DELAY) {} +BReverbModel::BReverbModel(const ReverbMode mode, const bool mt32CompatibleModel) : + allpasses(NULL), combs(NULL), + currentSettings(mt32CompatibleModel ? getMT32Settings(mode) : getCM32L_LAPCSettings(mode)), + tapDelayMode(mode == REVERB_MODE_TAP_DELAY) {} BReverbModel::~BReverbModel() { close(); @@ -334,12 +402,21 @@ void BReverbModel::setParameters(Bit8u time, Bit8u level) { if (time == 0 && level == 0) { dryAmp = wetLevel = 0; } else { - dryAmp = currentSettings.dryAmps[level]; + if (tapDelayMode && ((time == 0) || (time == 1 && level == 1))) { + // Looks like MT-32 implementation has some minor quirks in this mode: + // for odd level values, the output level changes sometimes depending on the time value which doesn't seem right. + dryAmp = currentSettings.dryAmps[level + 8]; + } else { + dryAmp = currentSettings.dryAmps[level]; + } wetLevel = currentSettings.wetLevels[level]; } } bool BReverbModel::isActive() const { + if (combs == NULL) { + return false; + } for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { if (!allpasses[i]->isEmpty()) return true; } @@ -349,14 +426,34 @@ bool BReverbModel::isActive() const { return false; } +bool BReverbModel::isMT32Compatible(const ReverbMode mode) const { + return ¤tSettings == &getMT32Settings(mode); +} + void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples) { + if (combs == NULL) { + Synth::muteSampleBuffer(outLeft, numSamples); + Synth::muteSampleBuffer(outRight, numSamples); + return; + } + Sample dry; - while (numSamples > 0) { + while ((numSamples--) > 0) { if (tapDelayMode) { - dry = *inLeft + *inRight; +#if MT32EMU_USE_FLOAT_SAMPLES + dry = (*(inLeft++) * 0.5f) + (*(inRight++) * 0.5f); +#else + dry = (*(inLeft++) >> 1) + (*(inRight++) >> 1); +#endif } else { - dry = *inLeft / 2 + *inRight / 2; +#if MT32EMU_USE_FLOAT_SAMPLES + dry = (*(inLeft++) * 0.25f) + (*(inRight++) * 0.25f); +#elif MT32EMU_BOSS_REVERB_PRECISE_MODE + dry = (*(inLeft++) >> 1) / 2 + (*(inRight++) >> 1) / 2; +#else + dry = (*(inLeft++) >> 2) + (*(inRight++) >> 2); +#endif } // Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I @@ -365,8 +462,12 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample * if (tapDelayMode) { TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs); comb->process(dry); - *outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF); - *outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF); + if (outLeft != NULL) { + *(outLeft++) = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF); + } + if (outRight != NULL) { + *(outRight++) = weirdMul(comb->getRightOutput(), wetLevel, 0xFF); + } } else { // If the output position is equal to the comb size, get it now in order not to loose it Sample link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); @@ -389,33 +490,38 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample * combs[2]->process(link); combs[3]->process(link); - Sample outL2 = combs[2]->getOutputAt(currentSettings.outLPositions[1]); - Sample outL3 = combs[3]->getOutputAt(currentSettings.outLPositions[2]); - Sample outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]); - Sample outR2 = combs[2]->getOutputAt(currentSettings.outRPositions[1]); - Sample outR3 = combs[3]->getOutputAt(currentSettings.outRPositions[2]); - + if (outLeft != NULL) { + Sample outL2 = combs[2]->getOutputAt(currentSettings.outLPositions[1]); + Sample outL3 = combs[3]->getOutputAt(currentSettings.outLPositions[2]); #if MT32EMU_USE_FLOAT_SAMPLES - *outLeft = 1.5f * (outL1 + outL2) + outL3; - *outRight = 1.5f * (outR1 + outR2) + outR3; + Sample outSample = 1.5f * (outL1 + outL2) + outL3; +#elif MT32EMU_BOSS_REVERB_PRECISE_MODE + /* NOTE: + * Thanks to Mok for discovering, the adder in BOSS reverb chip is found to perform addition with saturation to avoid integer overflow. + * Analysing of the algorithm suggests that the overflow is most probable when the combs output is added below. + * So, despite this isn't actually accurate, we only add the check here for performance reasons. + */ + Sample outSample = Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s((Bit32s)outL1 + Bit32s(outL1 >> 1)) + (Bit32s)outL2) + Bit32s(outL2 >> 1)) + (Bit32s)outL3); #else - outL1 += outL1 >> 1; - outL2 += outL2 >> 1; - *outLeft = outL1 + outL2 + outL3; - - outR1 += outR1 >> 1; - outR2 += outR2 >> 1; - *outRight = outR1 + outR2 + outR3; + Sample outSample = Synth::clipBit16s((Bit32s)outL1 + Bit32s(outL1 >> 1) + (Bit32s)outL2 + Bit32s(outL2 >> 1) + (Bit32s)outL3); +#endif + *(outLeft++) = weirdMul(outSample, wetLevel, 0xFF); + } + if (outRight != NULL) { + Sample outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]); + Sample outR2 = combs[2]->getOutputAt(currentSettings.outRPositions[1]); + Sample outR3 = combs[3]->getOutputAt(currentSettings.outRPositions[2]); +#if MT32EMU_USE_FLOAT_SAMPLES + Sample outSample = 1.5f * (outR1 + outR2) + outR3; +#elif MT32EMU_BOSS_REVERB_PRECISE_MODE + // See the note above for the left channel output. + Sample outSample = Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s((Bit32s)outR1 + Bit32s(outR1 >> 1)) + (Bit32s)outR2) + Bit32s(outR2 >> 1)) + (Bit32s)outR3); +#else + Sample outSample = Synth::clipBit16s((Bit32s)outR1 + Bit32s(outR1 >> 1) + (Bit32s)outR2 + Bit32s(outR2 >> 1) + (Bit32s)outR3); #endif - *outLeft = weirdMul(*outLeft, wetLevel, 0xFF); - *outRight = weirdMul(*outRight, wetLevel, 0xFF); + *(outRight++) = weirdMul(outSample, wetLevel, 0xFF); + } } - - numSamples--; - inLeft++; - inRight++; - outLeft++; - outRight++; } } diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h index 9efdc88f4d..9b840900c3 100644 --- a/audio/softsynth/mt32/BReverbModel.h +++ b/audio/softsynth/mt32/BReverbModel.h @@ -97,8 +97,11 @@ class BReverbModel { Bit32u wetLevel; void mute(); + static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode); + static const BReverbSettings &getMT32Settings(const ReverbMode mode); + public: - BReverbModel(const ReverbMode mode); + BReverbModel(const ReverbMode mode, const bool mt32CompatibleModel = false); ~BReverbModel(); // After construction or a close(), open() must be called at least once before any other call (with the exception of close()). void open(); @@ -107,6 +110,7 @@ public: void setParameters(Bit8u time, Bit8u level); void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples); bool isActive() const; + bool isMT32Compatible(const ReverbMode mode) const; }; } diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp index c782fd16a9..123ac0cc9d 100644 --- a/audio/softsynth/mt32/ROMInfo.cpp +++ b/audio/softsynth/mt32/ROMInfo.cpp @@ -20,15 +20,18 @@ namespace MT32Emu { +static const ControlROMFeatureSet MT32_COMPATIBLE(true); +static const ControlROMFeatureSet CM32L_COMPATIBLE(false); + // Known ROMs -static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, NULL}; -static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, NULL}; -static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, NULL}; -static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, NULL}; -static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; +static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; +static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; +static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; +static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; -static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, NULL}; -static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, &CM32L_COMPATIBLE}; +static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, &CM32L_COMPATIBLE}; static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL, NULL}; static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL, NULL}; @@ -108,4 +111,11 @@ const ROMInfo* ROMImage::getROMInfo() const { return romInfo; } +ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible) : defaultReverbMT32Compatible(useDefaultReverbMT32Compatible) { +} + +bool ControlROMFeatureSet::isDefaultReverbMT32Compatible() const { + return defaultReverbMT32Compatible; +} + } diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h index 4d679403c8..cecbb6054f 100644 --- a/audio/softsynth/mt32/ROMInfo.h +++ b/audio/softsynth/mt32/ROMInfo.h @@ -23,6 +23,8 @@ namespace MT32Emu { +struct ControlROMFeatureSet; + // Defines vital info about ROM file to be used by synth and applications struct ROMInfo { @@ -34,7 +36,7 @@ public: const char *description; enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; ROMInfo *pairROMInfo; - void *controlROMInfo; + const ControlROMFeatureSet *controlROMFeatures; // Returns a ROMInfo struct by inspecting the size and the SHA1 hash static const ROMInfo* getROMInfo(Common::File *file); @@ -72,6 +74,15 @@ public: const ROMInfo *getROMInfo() const; }; +struct ControlROMFeatureSet { +private: + unsigned int defaultReverbMT32Compatible : 1; + +public: + ControlROMFeatureSet(bool defaultReverbMT32Compatible); + bool isDefaultReverbMT32Compatible() const; +}; + } #endif diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 03446823e7..c6ecf47cad 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -20,13 +20,11 @@ //#include <cstdlib> //#include <cstring> -#define FORBIDDEN_SYMBOL_EXCEPTION_printf -#define FORBIDDEN_SYMBOL_EXCEPTION_vprintf - #include "mt32emu.h" #include "mmath.h" #include "PartialManager.h" #include "BReverbModel.h" +#include "common/debug.h" namespace MT32Emu { @@ -42,19 +40,6 @@ static const ControlROMMap ControlROMMaps[7] = { // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp) }; -static inline void muteStream(Sample *stream, Bit32u len) { - if (stream == NULL) return; - -#if MT32EMU_USE_FLOAT_SAMPLES - // FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win) - while (len--) { - *stream++ = 0.0f; - } -#else - memset(stream, 0, len * sizeof(Sample)); -#endif -} - static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) { if (stream != NULL) { stream += posDelta; @@ -74,9 +59,9 @@ Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) { Synth::Synth(ReportHandler *useReportHandler) { isOpen = false; - reverbEnabled = true; reverbOverridden = false; partialCount = DEFAULT_MAX_PARTIALS; + controlROMFeatures = NULL; if (useReportHandler == NULL) { reportHandler = new ReportHandler; @@ -86,11 +71,9 @@ Synth::Synth(ReportHandler *useReportHandler) { isDefaultReportHandler = false; } - reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM); - reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL); - reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE); - reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY); - + for (int i = 0; i < 4; i++) { + reverbModels[i] = NULL; + } reverbModel = NULL; setDACInputMode(DACInputMode_NICE); setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY); @@ -106,22 +89,19 @@ Synth::Synth(ReportHandler *useReportHandler) { Synth::~Synth() { close(); // Make sure we're closed and everything is freed - for (int i = 0; i < 4; i++) { - delete reverbModels[i]; - } if (isDefaultReportHandler) { delete reportHandler; } } void ReportHandler::showLCDMessage(const char *data) { - printf("WRITE-LCD: %s", data); - printf("\n"); + debug("WRITE-LCD: %s", data); + debug("\n"); } void ReportHandler::printDebug(const char *fmt, va_list list) { - vprintf(fmt, list); - printf("\n"); + debug(fmt, list); + debug("\n"); } void Synth::polyStateChanged(int partNum) { @@ -143,11 +123,22 @@ void Synth::printDebug(const char *fmt, ...) { } void Synth::setReverbEnabled(bool newReverbEnabled) { - reverbEnabled = newReverbEnabled; + if (isReverbEnabled() == newReverbEnabled) return; + if (newReverbEnabled) { + bool oldReverbOverridden = reverbOverridden; + reverbOverridden = false; + refreshSystemReverbParameters(); + reverbOverridden = oldReverbOverridden; + } else { +#if MT32EMU_REDUCE_REVERB_MEMORY + reverbModel->close(); +#endif + reverbModel = NULL; + } } bool Synth::isReverbEnabled() const { - return reverbEnabled; + return reverbModel != NULL; } void Synth::setReverbOverridden(bool newReverbOverridden) { @@ -158,7 +149,40 @@ bool Synth::isReverbOverridden() const { return reverbOverridden; } +void Synth::setReverbCompatibilityMode(bool mt32CompatibleMode) { + if (reverbModels[REVERB_MODE_ROOM] != NULL) { + if (isMT32ReverbCompatibilityMode() == mt32CompatibleMode) return; + setReverbEnabled(false); + for (int i = 0; i < 4; i++) { + delete reverbModels[i]; + } + } + reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode); + reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode); + reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode); + reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode); +#if !MT32EMU_REDUCE_REVERB_MEMORY + for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) { + reverbModels[i]->open(); + } +#endif + if (isOpen) { + setReverbOutputGain(reverbOutputGain); + setReverbEnabled(true); + } +} + +bool Synth::isMT32ReverbCompatibilityMode() const { + return isOpen && (reverbModels[REVERB_MODE_ROOM]->isMT32Compatible(REVERB_MODE_ROOM)); +} + void Synth::setDACInputMode(DACInputMode mode) { +#if MT32EMU_USE_FLOAT_SAMPLES + // We aren't emulating these in float mode, so better to inform the invoker + if ((mode == DACInputMode_GENERATION1) || (mode == DACInputMode_GENERATION2)) { + mode = DACInputMode_NICE; + } +#endif dacInputMode = mode; } @@ -174,10 +198,13 @@ MIDIDelayMode Synth::getMIDIDelayMode() const { return midiDelayMode; } -#if MT32EMU_USE_FLOAT_SAMPLES - void Synth::setOutputGain(float newOutputGain) { + if (newOutputGain < 0.0f) newOutputGain = -newOutputGain; outputGain = newOutputGain; +#if !MT32EMU_USE_FLOAT_SAMPLES + if (256.0f < newOutputGain) newOutputGain = 256.0f; + effectiveOutputGain = int(newOutputGain * 256.0f); +#endif } float Synth::getOutputGain() const { @@ -185,38 +212,21 @@ float Synth::getOutputGain() const { } void Synth::setReverbOutputGain(float newReverbOutputGain) { + if (newReverbOutputGain < 0.0f) newReverbOutputGain = -newReverbOutputGain; reverbOutputGain = newReverbOutputGain; + if (!isMT32ReverbCompatibilityMode()) newReverbOutputGain *= CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR; +#if MT32EMU_USE_FLOAT_SAMPLES + effectiveReverbOutputGain = newReverbOutputGain; +#else + if (256.0f < newReverbOutputGain) newReverbOutputGain = 256.0f; + effectiveReverbOutputGain = int(newReverbOutputGain * 256.0f); +#endif } float Synth::getReverbOutputGain() const { return reverbOutputGain; } -#else // #if MT32EMU_USE_FLOAT_SAMPLES - -void Synth::setOutputGain(float newOutputGain) { - if (newOutputGain < 0.0f) newOutputGain = -newOutputGain; - if (256.0f < newOutputGain) newOutputGain = 256.0f; - outputGain = int(newOutputGain * 256.0f); -} - -float Synth::getOutputGain() const { - return outputGain / 256.0f; -} - -void Synth::setReverbOutputGain(float newReverbOutputGain) { - if (newReverbOutputGain < 0.0f) newReverbOutputGain = -newReverbOutputGain; - float maxValue = 256.0f / CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR; - if (maxValue < newReverbOutputGain) newReverbOutputGain = maxValue; - reverbOutputGain = int(newReverbOutputGain * 256.0f); -} - -float Synth::getReverbOutputGain() const { - return reverbOutputGain / 256.0f; -} - -#endif // #if MT32EMU_USE_FLOAT_SAMPLES - void Synth::setReversedStereoEnabled(bool enabled) { reversedStereoEnabled = enabled; } @@ -234,6 +244,14 @@ bool Synth::loadControlROM(const ROMImage &controlROMImage) { || (controlROMInfo->pairType != ROMInfo::Full)) { return false; } + controlROMFeatures = controlROMImage.getROMInfo()->controlROMFeatures; + if (controlROMFeatures == NULL) { +#if MT32EMU_MONITOR_INIT + printDebug("Invalid Control ROM Info provided without feature set"); +#endif + return false; + } + #if MT32EMU_MONITOR_INIT printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description); #endif @@ -373,14 +391,6 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u } partialCount = usePartialCount; abortingPoly = NULL; -#if MT32EMU_MONITOR_INIT - printDebug("Initialising Constant Tables"); -#endif -#if !MT32EMU_REDUCE_REVERB_MEMORY - for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) { - reverbModels[i]->open(); - } -#endif // This is to help detect bugs memset(&mt32ram, '?', sizeof(mt32ram)); @@ -412,6 +422,15 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u } #if MT32EMU_MONITOR_INIT + printDebug("Initialising Reverb Models"); +#endif + bool mt32CompatibleReverb = controlROMFeatures->isDefaultReverbMT32Compatible(); +#if MT32EMU_MONITOR_INIT + printDebug("Using %s Compatible Reverb Models", mt32CompatibleReverb ? "MT-32" : "CM-32L"); +#endif + setReverbCompatibilityMode(mt32CompatibleReverb); + +#if MT32EMU_MONITOR_INIT printDebug("Initialising Timbre Bank A"); #endif if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0x40, 0, controlROMMap->timbreACompressed)) { @@ -484,7 +503,11 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u mt32ram.system.chanAssign[i] = i + 1; } mt32ram.system.masterVol = 100; // Confirmed + + bool oldReverbOverridden = reverbOverridden; + reverbOverridden = false; refreshSystem(); + reverbOverridden = oldReverbOverridden; for (int i = 0; i < 9; i++) { MemParams::PatchTemp *patchTemp = &mt32ram.patchTemp[i]; @@ -526,8 +549,8 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u return true; } -void Synth::close() { - if (!isOpen) { +void Synth::close(bool forced) { + if (!forced && !isOpen) { return; } @@ -548,9 +571,11 @@ void Synth::close() { deleteMemoryRegions(); for (int i = 0; i < 4; i++) { - reverbModels[i]->close(); + delete reverbModels[i]; + reverbModels[i] = NULL; } reverbModel = NULL; + controlROMFeatures = NULL; isOpen = false; } @@ -1249,7 +1274,7 @@ void Synth::refreshSystemReverbParameters() { #if MT32EMU_MONITOR_SYSEX > 0 printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel); #endif - if (reverbOverridden && reverbModel != NULL) { + if (reverbOverridden) { #if MT32EMU_MONITOR_SYSEX > 0 printDebug(" (Reverb overridden - ignoring)"); #endif @@ -1259,17 +1284,31 @@ void Synth::refreshSystemReverbParameters() { reportHandler->onNewReverbTime(mt32ram.system.reverbTime); reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel); - BReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode]; + BReverbModel *oldReverbModel = reverbModel; + if (mt32ram.system.reverbTime == 0 && mt32ram.system.reverbLevel == 0) { + // Setting both time and level to 0 effectively disables wet reverb output on real devices. + // Take a shortcut in this case to reduce CPU load. + reverbModel = NULL; + } else { + reverbModel = reverbModels[mt32ram.system.reverbMode]; + } + if (reverbModel != oldReverbModel) { #if MT32EMU_REDUCE_REVERB_MEMORY - if (reverbModel != newReverbModel) { - if (reverbModel != NULL) { - reverbModel->close(); + if (oldReverbModel != NULL) { + oldReverbModel->close(); + } + if (isReverbEnabled()) { + reverbModel->open(); + } +#else + if (isReverbEnabled()) { + reverbModel->mute(); } - newReverbModel->open(); - } #endif - reverbModel = newReverbModel; - reverbModel->setParameters(mt32ram.system.reverbTime, mt32ram.system.reverbLevel); + } + if (isReverbEnabled()) { + reverbModel->setParameters(mt32ram.system.reverbTime, mt32ram.system.reverbLevel); + } } void Synth::refreshSystemReserveSettings() { @@ -1468,94 +1507,103 @@ void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample } } -void Synth::convertSamplesToOutput(Sample *target, const Sample *source, Bit32u len, bool reverb) { - if (target == NULL) return; - - if (dacInputMode == DACInputMode_PURE) { - memcpy(target, source, len * sizeof(Sample)); - return; - } - -#if MT32EMU_USE_FLOAT_SAMPLES - float gain = reverb ? reverbOutputGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR : 2.0f * outputGain; - while (len--) { - *(target++) = *(source++) * gain; - } -#else - int gain; - if (reverb) { - gain = int(reverbOutputGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR); - } else { - gain = outputGain; - switch (dacInputMode) { - case DACInputMode_NICE: - // Since we're not shooting for accuracy here, don't worry about the rounding mode. - gain <<= 1; - break; - case DACInputMode_GENERATION1: +// In GENERATION2 units, the output from LA32 goes to the Boss chip already bit-shifted. +// In NICE mode, it's also better to increase volume before the reverb processing to preserve accuracy. +void Synth::produceLA32Output(Sample *buffer, Bit32u len) { +#if !MT32EMU_USE_FLOAT_SAMPLES + switch (dacInputMode) { + case DACInputMode_GENERATION2: while (len--) { - *target = clipBit16s(Bit32s((*source * gain) >> 8)); - *target = (*target & 0x8000) | ((*target << 1) & 0x7FFE); - source++; - target++; + *buffer = (*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE) | ((*buffer >> 14) & 0x0001); + ++buffer; } - return; - case DACInputMode_GENERATION2: + break; + case DACInputMode_NICE: while (len--) { - *target = clipBit16s(Bit32s((*source * gain) >> 8)); - *target = (*target & 0x8000) | ((*target << 1) & 0x7FFE) | ((*target >> 14) & 0x0001); - source++; - target++; + *buffer = clipBit16s(Bit32s(*buffer) << 1); + ++buffer; } - return; + break; default: break; + } +#endif +} + +void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb) { + if (dacInputMode == DACInputMode_PURE) return; + +#if MT32EMU_USE_FLOAT_SAMPLES + float gain = reverb ? effectiveReverbOutputGain : outputGain; + while (len--) { + *(buffer++) *= gain; + } +#else + int gain = reverb ? effectiveReverbOutputGain : effectiveOutputGain; + if (dacInputMode == DACInputMode_GENERATION1) { + while (len--) { + Bit32s target = Bit16s((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE)); + *(buffer++) = clipBit16s((target * gain) >> 8); } + return; } while (len--) { - *(target++) = clipBit16s(Bit32s((*(source++) * gain) >> 8)); + *buffer = clipBit16s((Bit32s(*buffer) * gain) >> 8); + ++buffer; } #endif } void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) { + // Even if LA32 output isn't desired, we proceed anyway with temp buffers + Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN]; + if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft; + if (nonReverbLeft == NULL) nonReverbRight = tmpBufNonReverbRight; + + Sample tmpBufReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbDryRight[MAX_SAMPLES_PER_RUN]; + if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft; + if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight; + + muteSampleBuffer(nonReverbLeft, len); + muteSampleBuffer(nonReverbRight, len); + muteSampleBuffer(reverbDryLeft, len); + muteSampleBuffer(reverbDryRight, len); + if (isEnabled) { - Sample tmpBufMixLeft[MAX_SAMPLES_PER_RUN], tmpBufMixRight[MAX_SAMPLES_PER_RUN]; - muteStream(tmpBufMixLeft, len); - muteStream(tmpBufMixRight, len); for (unsigned int i = 0; i < getPartialCount(); i++) { - if (!reverbEnabled || !partialManager->shouldReverb(i)) { - partialManager->produceOutput(i, tmpBufMixLeft, tmpBufMixRight, len); + if (partialManager->shouldReverb(i)) { + partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len); + } else { + partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len); } } - convertSamplesToOutput(nonReverbLeft, tmpBufMixLeft, len, false); - convertSamplesToOutput(nonReverbRight, tmpBufMixRight, len, false); - } else { - muteStream(nonReverbLeft, len); - muteStream(nonReverbRight, len); - } - if (isEnabled && reverbEnabled) { - Sample tmpBufMixLeft[MAX_SAMPLES_PER_RUN], tmpBufMixRight[MAX_SAMPLES_PER_RUN]; - muteStream(tmpBufMixLeft, len); - muteStream(tmpBufMixRight, len); - for (unsigned int i = 0; i < getPartialCount(); i++) { - if (partialManager->shouldReverb(i)) { - partialManager->produceOutput(i, tmpBufMixLeft, tmpBufMixRight, len); - } + produceLA32Output(reverbDryLeft, len); + produceLA32Output(reverbDryRight, len); + + if (isReverbEnabled()) { + reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len); + if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len, true); + if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len, true); + } else { + muteSampleBuffer(reverbWetLeft, len); + muteSampleBuffer(reverbWetRight, len); } - convertSamplesToOutput(reverbDryLeft, tmpBufMixLeft, len, false); - convertSamplesToOutput(reverbDryRight, tmpBufMixRight, len, false); - Sample tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN]; - reverbModel->process(tmpBufMixLeft, tmpBufMixRight, tmpBufReverbOutLeft, tmpBufReverbOutRight, len); - convertSamplesToOutput(reverbWetLeft, tmpBufReverbOutLeft, len, true); - convertSamplesToOutput(reverbWetRight, tmpBufReverbOutRight, len, true); + // Don't bother with conversion if the output is going to be unused + if (nonReverbLeft != tmpBufNonReverbLeft) { + produceLA32Output(nonReverbLeft, len); + convertSamplesToOutput(nonReverbLeft, len, false); + } + if (nonReverbRight != tmpBufNonReverbRight) { + produceLA32Output(nonReverbRight, len); + convertSamplesToOutput(nonReverbRight, len, false); + } + if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len, false); + if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len, false); } else { - muteStream(reverbDryLeft, len); - muteStream(reverbDryRight, len); - muteStream(reverbWetLeft, len); - muteStream(reverbWetRight, len); + muteSampleBuffer(reverbWetLeft, len); + muteSampleBuffer(reverbWetRight, len); } partialManager->clearAlreadyOutputed(); @@ -1589,7 +1637,7 @@ bool Synth::isActive() const { if (hasActivePartials()) { return true; } - if (reverbEnabled) { + if (isReverbEnabled()) { return reverbModel->isActive(); } return false; diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index 25342089b8..37fb7b280a 100644 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -322,6 +322,7 @@ private: PCMWaveEntry *pcmWaves; // Array + const ControlROMFeatureSet *controlROMFeatures; const ControlROMMap *controlROMMap; Bit8u controlROMData[CONTROL_ROM_SIZE]; Bit16s *pcmROMData; @@ -338,18 +339,18 @@ private: BReverbModel *reverbModels[4]; BReverbModel *reverbModel; - bool reverbEnabled; bool reverbOverridden; MIDIDelayMode midiDelayMode; DACInputMode dacInputMode; -#if MT32EMU_USE_FLOAT_SAMPLES float outputGain; float reverbOutputGain; +#if MT32EMU_USE_FLOAT_SAMPLES + float effectiveReverbOutputGain; #else - int outputGain; - int reverbOutputGain; + int effectiveOutputGain; + int effectiveReverbOutputGain; #endif bool reversedStereoEnabled; @@ -370,7 +371,8 @@ private: Bit32u getShortMessageLength(Bit32u msg); Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp); - void convertSamplesToOutput(Sample *target, const Sample *source, Bit32u len, bool reverb); + void produceLA32Output(Sample *buffer, Bit32u len); + void convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb); bool isAbortingPoly() const; void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); @@ -405,10 +407,23 @@ private: public: static inline Bit16s clipBit16s(Bit32s sample) { // Clamp values above 32767 to 32767, and values below -32768 to -32768 - if ((sample + 32768) & ~65535) { - return (sample >> 31) ^ 32767; + // FIXME: Do we really need this stuff? I think these branches are very well predicted. Instead, this introduces a chain. + // The version below is actually a bit faster on my system... + //return ((sample + 0x8000) & ~0xFFFF) ? (sample >> 31) ^ 0x7FFF : (Bit16s)sample; + return ((-0x8000 <= sample) && (sample <= 0x7FFF)) ? (Bit16s)sample : (sample >> 31) ^ 0x7FFF; + } + + static inline void muteSampleBuffer(Sample *buffer, Bit32u len) { + if (buffer == NULL) return; + +#if MT32EMU_USE_FLOAT_SAMPLES + // FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win) + while (len--) { + *(buffer++) = 0.0f; } - return (Bit16s)sample; +#else + memset(buffer, 0, len * sizeof(Sample)); +#endif } static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); @@ -424,7 +439,7 @@ public: bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS); // Closes the MT-32 and deallocates any memory used by the synthesizer - void close(void); + void close(bool forced = false); // All the enqueued events are processed by the synth immediately. void flushMIDIQueue(); @@ -464,8 +479,17 @@ public: void setReverbEnabled(bool reverbEnabled); bool isReverbEnabled() const; + // Sets override reverb mode. In this mode, emulation ignores sysexes (or the related part of them) which control the reverb parameters. + // This mode is in effect until it is turned off. When the synth is re-opened, the override mode is unchanged but the state + // of the reverb model is reset to default. void setReverbOverridden(bool reverbOverridden); bool isReverbOverridden() const; + // Forces reverb model compatibility mode. By default, the compatibility mode corresponds to the used control ROM version. + // Invoking this method with the argument set to true forces emulation of old MT-32 reverb circuit. + // When the argument is false, emulation of the reverb circuit used in new generation of MT-32 compatible modules is enforced + // (these include CM-32L and LAPC-I). + void setReverbCompatibilityMode(bool mt32CompatibleMode); + bool isMT32ReverbCompatibilityMode() const; void setDACInputMode(DACInputMode mode); DACInputMode getDACInputMode() const; void setMIDIDelayMode(MIDIDelayMode mode); |