aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/Synth.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'audio/softsynth/mt32/Synth.cpp')
-rw-r--r--audio/softsynth/mt32/Synth.cpp338
1 files changed, 193 insertions, 145 deletions
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;