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.cpp248
1 files changed, 159 insertions, 89 deletions
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 3bff429875..6df7eb9e31 100644
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -22,12 +22,19 @@
#include "mt32emu.h"
#include "mmath.h"
-#include "PartialManager.h"
+#include "internals.h"
+
+#include "Analog.h"
#include "BReverbModel.h"
-#include "common/debug.h"
+#include "MemoryRegion.h"
+#include "MidiEventQueue.h"
+#include "PartialManager.h"
namespace MT32Emu {
+// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
+static const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
+
static const ControlROMMap ControlROMMaps[7] = {
// ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO, tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax
{0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
@@ -46,18 +53,15 @@ static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) {
}
}
-Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
+Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum) {
+ unsigned int checksum = -initChecksum;
for (unsigned int i = 0; i < len; i++) {
- checksum = checksum + data[i];
+ checksum -= data[i];
}
- checksum = checksum & 0x7f;
- if (checksum) {
- checksum = 0x80 - checksum;
- }
- return checksum;
+ return Bit8u(checksum & 0x7f);
}
-Synth::Synth(ReportHandler *useReportHandler) {
+Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32default(*new MemParams()) {
isOpen = false;
reverbOverridden = false;
partialCount = DEFAULT_MAX_PARTIALS;
@@ -75,6 +79,7 @@ Synth::Synth(ReportHandler *useReportHandler) {
reverbModels[i] = NULL;
}
reverbModel = NULL;
+ analog = NULL;
setDACInputMode(DACInputMode_NICE);
setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY);
setOutputGain(1.0f);
@@ -92,6 +97,8 @@ Synth::~Synth() {
if (isDefaultReportHandler) {
delete reportHandler;
}
+ delete &mt32ram;
+ delete &mt32default;
}
void ReportHandler::showLCDMessage(const char *data) {
@@ -126,7 +133,7 @@ void Synth::printDebug(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
#if MT32EMU_DEBUG_SAMPLESTAMPS > 0
- reportHandler->printDebug("[%u] ", renderedSampleCount);
+ reportHandler->printDebug("[%u] ", (char *)&renderedSampleCount);
#endif
reportHandler->printDebug(fmt, ap);
va_end(ap);
@@ -211,10 +218,7 @@ MIDIDelayMode Synth::getMIDIDelayMode() const {
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
+ if (analog != NULL) analog->setSynthOutputGain(newOutputGain);
}
float Synth::getOutputGain() const {
@@ -224,13 +228,7 @@ 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
+ if (analog != NULL) analog->setReverbOutputGain(newReverbOutputGain, isMT32ReverbCompatibilityMode());
}
float Synth::getReverbOutputGain() const {
@@ -393,7 +391,11 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi
return true;
}
-bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount) {
+bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode) {
+ return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode);
+}
+
+bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) {
if (isOpen) {
return false;
}
@@ -548,6 +550,10 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
midiQueue = new MidiEventQueue();
+ analog = new Analog(analogOutputMode, controlROMFeatures);
+ setOutputGain(outputGain);
+ setReverbOutputGain(reverbOutputGain);
+
isOpen = true;
isEnabled = false;
@@ -565,6 +571,9 @@ void Synth::close(bool forced) {
delete midiQueue;
midiQueue = NULL;
+ delete analog;
+ analog = NULL;
+
delete partialManager;
partialManager = NULL;
@@ -603,16 +612,37 @@ void Synth::flushMIDIQueue() {
}
}
-void Synth::setMIDIEventQueueSize(Bit32u useSize) {
- if (midiQueue != NULL) {
- flushMIDIQueue();
- delete midiQueue;
- midiQueue = new MidiEventQueue(useSize);
+Bit32u Synth::setMIDIEventQueueSize(Bit32u useSize) {
+ static const Bit32u MAX_QUEUE_SIZE = (1 << 24); // This results in about 256 Mb - much greater than any reasonable value
+
+ if (midiQueue == NULL) return 0;
+ flushMIDIQueue();
+
+ // Find a power of 2 that is >= useSize
+ Bit32u binarySize = 1;
+ if (useSize < MAX_QUEUE_SIZE) {
+ // Using simple linear search as this isn't time critical
+ while (binarySize < useSize) binarySize <<= 1;
+ } else {
+ binarySize = MAX_QUEUE_SIZE;
}
+ delete midiQueue;
+ midiQueue = new MidiEventQueue(binarySize);
+ return binarySize;
}
Bit32u Synth::getShortMessageLength(Bit32u msg) {
- if ((msg & 0xF0) == 0xF0) return 1;
+ if ((msg & 0xF0) == 0xF0) {
+ switch (msg & 0xFF) {
+ case 0xF1:
+ case 0xF3:
+ return 2;
+ case 0xF2:
+ return 3;
+ default:
+ return 1;
+ }
+ }
// NOTE: This calculation isn't quite correct
// as it doesn't consider the running status byte
return ((msg & 0xE0) == 0xC0) ? 2 : 3;
@@ -638,6 +668,7 @@ bool Synth::playMsg(Bit32u msg, Bit32u timestamp) {
if (midiDelayMode != MIDIDelayMode_IMMEDIATE) {
timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp);
}
+ if (!isEnabled) isEnabled = true;
return midiQueue->pushShortMessage(msg, timestamp);
}
@@ -650,16 +681,19 @@ bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) {
if (midiDelayMode == MIDIDelayMode_DELAY_ALL) {
timestamp = addMIDIInterfaceDelay(len, timestamp);
}
+ if (!isEnabled) isEnabled = true;
return midiQueue->pushSysex(sysex, len, timestamp);
}
void Synth::playMsgNow(Bit32u msg) {
- // FIXME: Implement active sensing
+ // NOTE: Active sense IS implemented in real hardware. However, realtime processing is clearly out of the library scope.
+ // It is assumed that realtime consumers of the library respond to these MIDI events as appropriate.
+
unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
unsigned char chan = (unsigned char)(msg & 0x00000F);
unsigned char note = (unsigned char)((msg & 0x007F00) >> 8);
unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16);
- isEnabled = true;
+ if (!isEnabled) isEnabled = true;
//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
@@ -831,7 +865,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command,
printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
return;
}
- unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
+ Bit8u checksum = calcSysexChecksum(sysex, len - 1);
if (checksum != sysex[len - 1]) {
printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
return;
@@ -1410,9 +1444,8 @@ void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32
memcpy(dstSysexData, useSysexData, sysexLength);
}
-MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBufferSize(useRingBufferSize) {
- ringBuffer = new MidiEvent[ringBufferSize];
- memset(ringBuffer, 0, ringBufferSize * sizeof(MidiEvent));
+MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBuffer(new MidiEvent[useRingBufferSize]), ringBufferMask(useRingBufferSize - 1) {
+ memset(ringBuffer, 0, useRingBufferSize * sizeof(MidiEvent));
reset();
}
@@ -1426,7 +1459,7 @@ void MidiEventQueue::reset() {
}
bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) {
- unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
+ Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
// Is ring buffer full?
if (startPosition == newEndPosition) return false;
ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp);
@@ -1435,7 +1468,7 @@ bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp)
}
bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) {
- unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
+ Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
// Is ring buffer full?
if (startPosition == newEndPosition) return false;
ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp);
@@ -1450,31 +1483,36 @@ const MidiEvent *MidiEventQueue::peekMidiEvent() {
void MidiEventQueue::dropMidiEvent() {
// Is ring buffer empty?
if (startPosition != endPosition) {
- startPosition = (startPosition + 1) % ringBufferSize;
+ startPosition = (startPosition + 1) & ringBufferMask;
}
}
+bool MidiEventQueue::isFull() const {
+ return startPosition == ((endPosition + 1) & ringBufferMask);
+}
+
+unsigned int Synth::getStereoOutputSampleRate() const {
+ return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate();
+}
+
void Synth::render(Sample *stream, Bit32u len) {
- Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN];
- Sample tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
- Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN];
- Sample tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
- Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN];
- Sample tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
+ if (!isEnabled) {
+ renderedSampleCount += analog->getDACStreamsLength(len);
+ analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len);
+ muteSampleBuffer(stream, len << 1);
+ return;
+ }
+
+ // As in AnalogOutputMode_ACCURATE mode output is upsampled, buffer size MAX_SAMPLES_PER_RUN is more than enough.
+ Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
+ Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
+ Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
while (len > 0) {
- Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
- renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisLen);
- for (Bit32u i = 0; i < thisLen; i++) {
-#if MT32EMU_USE_FLOAT_SAMPLES
- *(stream++) = tmpNonReverbLeft[i] + tmpReverbDryLeft[i] + tmpReverbWetLeft[i];
- *(stream++) = tmpNonReverbRight[i] + tmpReverbDryRight[i] + tmpReverbWetRight[i];
-#else
- *(stream++) = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]);
- *(stream++) = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]);
-#endif
- }
- len -= thisLen;
+ Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
+ renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, analog->getDACStreamsLength(thisPassLen));
+ analog->process(&stream, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
+ len -= thisPassLen;
}
}
@@ -1518,7 +1556,10 @@ void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample
// 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
+#if MT32EMU_USE_FLOAT_SAMPLES
+ (void)buffer;
+ (void)len;
+#else
switch (dacInputMode) {
case DACInputMode_GENERATION2:
while (len--) {
@@ -1528,7 +1569,7 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
break;
case DACInputMode_NICE:
while (len--) {
- *buffer = clipBit16s(Bit32s(*buffer) << 1);
+ *buffer = clipSampleEx(SampleEx(*buffer) << 1);
++buffer;
}
break;
@@ -1538,26 +1579,16 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
#endif
}
-void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb) {
- if (dacInputMode == DACInputMode_PURE) return;
-
+void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
- float gain = reverb ? effectiveReverbOutputGain : outputGain;
- while (len--) {
- *(buffer++) *= gain;
- }
+ (void)buffer;
+ (void)len;
#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);
+ *buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
+ ++buffer;
}
- return;
- }
- while (len--) {
- *buffer = clipBit16s((Bit32s(*buffer) * gain) >> 8);
- ++buffer;
}
#endif
}
@@ -1566,18 +1597,18 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
// 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;
+ if (nonReverbRight == 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) {
+ muteSampleBuffer(nonReverbLeft, len);
+ muteSampleBuffer(nonReverbRight, len);
+ muteSampleBuffer(reverbDryLeft, len);
+ muteSampleBuffer(reverbDryRight, len);
+
for (unsigned int i = 0; i < getPartialCount(); i++) {
if (partialManager->shouldReverb(i)) {
partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len);
@@ -1591,8 +1622,8 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
if (isReverbEnabled()) {
reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len);
- if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len, true);
- if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len, true);
+ if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len);
+ if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len);
} else {
muteSampleBuffer(reverbWetLeft, len);
muteSampleBuffer(reverbWetRight, len);
@@ -1601,15 +1632,20 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
// Don't bother with conversion if the output is going to be unused
if (nonReverbLeft != tmpBufNonReverbLeft) {
produceLA32Output(nonReverbLeft, len);
- convertSamplesToOutput(nonReverbLeft, len, false);
+ convertSamplesToOutput(nonReverbLeft, len);
}
if (nonReverbRight != tmpBufNonReverbRight) {
produceLA32Output(nonReverbRight, len);
- convertSamplesToOutput(nonReverbRight, len, false);
+ convertSamplesToOutput(nonReverbRight, len);
}
- if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len, false);
- if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len, false);
+ if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len);
+ if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len);
} else {
+ // Avoid muting buffers that wasn't requested
+ if (nonReverbLeft != tmpBufNonReverbLeft) muteSampleBuffer(nonReverbLeft, len);
+ if (nonReverbRight != tmpBufNonReverbRight) muteSampleBuffer(nonReverbRight, len);
+ if (reverbDryLeft != tmpBufReverbDryLeft) muteSampleBuffer(reverbDryLeft, len);
+ if (reverbDryRight != tmpBufReverbDryRight) muteSampleBuffer(reverbDryRight, len);
muteSampleBuffer(reverbWetLeft, len);
muteSampleBuffer(reverbWetRight, len);
}
@@ -1651,14 +1687,48 @@ bool Synth::isActive() const {
return false;
}
-const Partial *Synth::getPartial(unsigned int partialNum) const {
- return partialManager->getPartial(partialNum);
-}
-
unsigned int Synth::getPartialCount() const {
return partialCount;
}
+void Synth::getPartStates(bool *partStates) const {
+ for (int partNumber = 0; partNumber < 9; partNumber++) {
+ const Part *part = parts[partNumber];
+ partStates[partNumber] = part->getActiveNonReleasingPartialCount() > 0;
+ }
+}
+
+void Synth::getPartialStates(PartialState *partialStates) const {
+ static const PartialState partialPhaseToState[8] = {
+ PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK,
+ PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE
+ };
+
+ for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
+ const Partial *partial = partialManager->getPartial(partialNum);
+ partialStates[partialNum] = partial->isActive() ? partialPhaseToState[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
+ }
+}
+
+unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const {
+ unsigned int playingNotes = 0;
+ if (isOpen && (partNumber < 9)) {
+ const Part *part = parts[partNumber];
+ const Poly *poly = part->getFirstActivePoly();
+ while (poly != NULL) {
+ keys[playingNotes] = (Bit8u)poly->getKey();
+ velocities[playingNotes] = (Bit8u)poly->getVelocity();
+ playingNotes++;
+ poly = poly->getNext();
+ }
+ }
+ return playingNotes;
+}
+
+const char *Synth::getPatchName(unsigned int partNumber) const {
+ return (!isOpen || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr();
+}
+
const Part *Synth::getPart(unsigned int partNum) const {
if (partNum > 8) {
return NULL;