aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2012-02-09 11:20:45 +0200
committerEugene Sandulenko2012-02-09 11:31:03 +0200
commit030e155eeb7f82dc89315dbefa43e09a411c6110 (patch)
tree3b941d9841735fcc9878ab0663406d6be7a025aa
parent06b52994619fb9a7ef9e54e50b5cc67d07f6a0a0 (diff)
downloadscummvm-rg350-030e155eeb7f82dc89315dbefa43e09a411c6110.tar.gz
scummvm-rg350-030e155eeb7f82dc89315dbefa43e09a411c6110.tar.bz2
scummvm-rg350-030e155eeb7f82dc89315dbefa43e09a411c6110.zip
MT32: Update MT-32 emulator to latest Munt code
Several changes against original code were made. They were intentionally kept to the minimum
-rw-r--r--audio/softsynth/mt32.cpp42
-rw-r--r--audio/softsynth/mt32/AReverbModel.cpp237
-rw-r--r--audio/softsynth/mt32/AReverbModel.h86
-rw-r--r--audio/softsynth/mt32/DelayReverb.cpp150
-rw-r--r--audio/softsynth/mt32/DelayReverb.h53
-rw-r--r--audio/softsynth/mt32/FreeverbModel.cpp78
-rw-r--r--audio/softsynth/mt32/FreeverbModel.h44
-rw-r--r--audio/softsynth/mt32/LA32Ramp.cpp150
-rw-r--r--audio/softsynth/mt32/LA32Ramp.h43
-rw-r--r--audio/softsynth/mt32/Part.cpp622
-rw-r--r--audio/softsynth/mt32/Part.h133
-rw-r--r--audio/softsynth/mt32/Partial.cpp557
-rw-r--r--audio/softsynth/mt32/Partial.h119
-rw-r--r--audio/softsynth/mt32/PartialManager.cpp250
-rw-r--r--audio/softsynth/mt32/PartialManager.h54
-rw-r--r--audio/softsynth/mt32/Poly.cpp174
-rw-r--r--audio/softsynth/mt32/Poly.h67
-rw-r--r--audio/softsynth/mt32/Structures.h217
-rw-r--r--audio/softsynth/mt32/Synth.cpp1620
-rw-r--r--audio/softsynth/mt32/Synth.h471
-rw-r--r--audio/softsynth/mt32/TVA.cpp365
-rw-r--r--audio/softsynth/mt32/TVA.h94
-rw-r--r--audio/softsynth/mt32/TVF.cpp230
-rw-r--r--audio/softsynth/mt32/TVF.h54
-rw-r--r--audio/softsynth/mt32/TVP.cpp321
-rw-r--r--audio/softsynth/mt32/TVP.h67
-rw-r--r--audio/softsynth/mt32/Tables.cpp119
-rw-r--r--audio/softsynth/mt32/Tables.h64
-rw-r--r--audio/softsynth/mt32/freeverb.cpp357
-rw-r--r--audio/softsynth/mt32/freeverb.h322
-rw-r--r--audio/softsynth/mt32/i386.cpp849
-rw-r--r--audio/softsynth/mt32/i386.h49
-rw-r--r--audio/softsynth/mt32/mmath.h73
-rw-r--r--audio/softsynth/mt32/module.mk20
-rw-r--r--audio/softsynth/mt32/mt32_file.cpp69
-rw-r--r--audio/softsynth/mt32/mt32_file.h52
-rw-r--r--audio/softsynth/mt32/mt32emu.h140
-rw-r--r--audio/softsynth/mt32/part.cpp633
-rw-r--r--audio/softsynth/mt32/part.h112
-rw-r--r--audio/softsynth/mt32/partial.cpp968
-rw-r--r--audio/softsynth/mt32/partial.h148
-rw-r--r--audio/softsynth/mt32/partialManager.cpp272
-rw-r--r--audio/softsynth/mt32/partialManager.h56
-rw-r--r--audio/softsynth/mt32/structures.h284
-rw-r--r--audio/softsynth/mt32/synth.cpp1202
-rw-r--r--audio/softsynth/mt32/synth.h299
-rw-r--r--audio/softsynth/mt32/tables.cpp761
-rw-r--r--audio/softsynth/mt32/tables.h116
48 files changed, 6942 insertions, 6321 deletions
diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp
index eabde21296..c319457ae2 100644
--- a/audio/softsynth/mt32.cpp
+++ b/audio/softsynth/mt32.cpp
@@ -84,42 +84,6 @@ public:
int getRate() const { return _outputRate; }
};
-class MT32File : public MT32Emu::File {
- Common::File _in;
- Common::DumpFile _out;
-public:
- bool open(const char *filename, OpenMode mode) {
- if (mode == OpenMode_read)
- return _in.open(filename);
- else
- return _out.open(filename);
- }
- void close() {
- _in.close();
- _out.close();
- }
- size_t read(void *in, size_t size) {
- return _in.read(in, size);
- }
- bool readBit8u(MT32Emu::Bit8u *in) {
- byte b = _in.readByte();
- if (_in.eos())
- return false;
- *in = b;
- return true;
- }
- size_t write(const void *in, size_t size) {
- return _out.write(in, size);
- }
- bool writeBit8u(MT32Emu::Bit8u out) {
- _out.writeByte(out);
- return !_out.err();
- }
- bool isEOF() {
- return _in.isOpen() && _in.eos();
- }
-};
-
static int eatSystemEvents() {
Common::Event event;
Common::EventManager *eventMan = g_system->getEventManager();
@@ -206,9 +170,9 @@ static void drawMessage(int offset, const Common::String &text) {
g_system->updateScreen();
}
-static MT32Emu::File *MT32_OpenFile(void *userData, const char *filename, MT32Emu::File::OpenMode mode) {
- MT32File *file = new MT32File();
- if (!file->open(filename, mode)) {
+static Common::File *MT32_OpenFile(void *userData, const char *filename) {
+ Common::File *file = new Common::File();
+ if (!file->open(filename)) {
delete file;
return NULL;
}
diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp
new file mode 100644
index 0000000000..4ee6c87943
--- /dev/null
+++ b/audio/softsynth/mt32/AReverbModel.cpp
@@ -0,0 +1,237 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mt32emu.h"
+#include "AReverbModel.h"
+
+using namespace MT32Emu;
+
+// Default reverb settings for modes 0-2
+
+static const unsigned int NUM_ALLPASSES = 6;
+static const unsigned int NUM_DELAYS = 5;
+
+static const Bit32u MODE_0_ALLPASSES[] = {729, 78, 394, 994, 1250, 1889};
+static const Bit32u MODE_0_DELAYS[] = {846, 4, 1819, 778, 346};
+static const float MODE_0_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.9f};
+static const float MODE_0_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f};
+
+static const Bit32u MODE_1_ALLPASSES[] = {176, 809, 1324, 1258};
+static const Bit32u MODE_1_DELAYS[] = {2262, 124, 974, 2516, 356};
+static const float MODE_1_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.95f};
+static const float MODE_1_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f};
+
+static const Bit32u MODE_2_ALLPASSES[] = {78, 729, 994, 389};
+static const Bit32u MODE_2_DELAYS[] = {846, 4, 1819, 778, 346};
+static const float MODE_2_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f};
+static const float MODE_2_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f};
+
+const AReverbSettings AReverbModel::REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_DELAYS, MODE_0_TIMES, MODE_0_LEVELS, 0.687770909f, 0.5f, 0.5f};
+const AReverbSettings AReverbModel::REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_DELAYS, MODE_1_TIMES, MODE_1_LEVELS, 0.712025098f, 0.375f, 0.625f};
+const AReverbSettings AReverbModel::REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_DELAYS, MODE_2_TIMES, MODE_2_LEVELS, 0.939522749f, 0.0f, 0.0f};
+
+RingBuffer::RingBuffer(Bit32u newsize) {
+ index = 0;
+ size = newsize;
+ buffer = new float[size];
+}
+
+RingBuffer::~RingBuffer() {
+ delete[] buffer;
+ buffer = NULL;
+ size = 0;
+}
+
+float RingBuffer::next() {
+ index++;
+ if (index >= size) {
+ index = 0;
+ }
+ return buffer[index];
+}
+
+bool RingBuffer::isEmpty() {
+ if (buffer == NULL) return true;
+
+ float *buf = buffer;
+ float total = 0;
+ for (Bit32u i = 0; i < size; i++) {
+ total += (*buf < 0 ? -*buf : *buf);
+ buf++;
+ }
+ return ((total / size) < .0002 ? true : false);
+}
+
+void RingBuffer::mute() {
+ float *buf = buffer;
+ for (Bit32u i = 0; i < size; i++) {
+ *buf++ = 0;
+ }
+}
+
+AllpassFilter::AllpassFilter(Bit32u useSize) : RingBuffer(useSize) {
+}
+
+Delay::Delay(Bit32u useSize) : RingBuffer(useSize) {
+}
+
+float AllpassFilter::process(float in) {
+ // This model corresponds to the allpass filter implementation in the real CM-32L device
+ // found from sample analysis
+
+ float out;
+
+ out = next();
+
+ // store input - feedback / 2
+ buffer[index] = in - 0.5f * out;
+
+ // return buffer output + feedforward / 2
+ return out + 0.5f * buffer[index];
+}
+
+float Delay::process(float in) {
+ // Implements a very simple delay
+
+ float out;
+
+ out = next();
+
+ // store input
+ buffer[index] = in;
+
+ // return buffer output
+ return out;
+}
+
+AReverbModel::AReverbModel(const AReverbSettings *useSettings) : allpasses(NULL), delays(NULL), currentSettings(useSettings) {
+}
+
+AReverbModel::~AReverbModel() {
+ close();
+}
+
+void AReverbModel::open(unsigned int /*sampleRate*/) {
+ // FIXME: filter sizes must be multiplied by sample rate to 32000Hz ratio
+ // IIR filter values depend on sample rate as well
+ allpasses = new AllpassFilter*[NUM_ALLPASSES];
+ for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
+ allpasses[i] = new AllpassFilter(currentSettings->allpassSizes[i]);
+ }
+ delays = new Delay*[NUM_DELAYS];
+ for (Bit32u i = 0; i < NUM_DELAYS; i++) {
+ delays[i] = new Delay(currentSettings->delaySizes[i]);
+ }
+ mute();
+}
+
+void AReverbModel::close() {
+ if (allpasses != NULL) {
+ for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
+ if (allpasses[i] != NULL) {
+ delete allpasses[i];
+ allpasses[i] = NULL;
+ }
+ }
+ delete[] allpasses;
+ allpasses = NULL;
+ }
+ if (delays != NULL) {
+ for (Bit32u i = 0; i < NUM_DELAYS; i++) {
+ if (delays[i] != NULL) {
+ delete delays[i];
+ delays[i] = NULL;
+ }
+ }
+ delete[] delays;
+ delays = NULL;
+ }
+}
+
+void AReverbModel::mute() {
+ for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
+ allpasses[i]->mute();
+ }
+ for (Bit32u i = 0; i < NUM_DELAYS; i++) {
+ delays[i]->mute();
+ }
+ filterhist1 = 0;
+ filterhist2 = 0;
+ combhist = 0;
+}
+
+void AReverbModel::setParameters(Bit8u time, Bit8u level) {
+// FIXME: wetLevel definitely needs ramping when changed
+// Although, most games don't set reverb level during MIDI playback
+ decayTime = currentSettings->decayTimes[time];
+ wetLevel = currentSettings->wetLevels[level];
+}
+
+bool AReverbModel::isActive() const {
+ bool bActive = false;
+ for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
+ bActive |= !allpasses[i]->isEmpty();
+ }
+ for (Bit32u i = 0; i < NUM_DELAYS; i++) {
+ bActive |= !delays[i]->isEmpty();
+ }
+ return bActive;
+}
+
+void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
+// Three series allpass filters followed by a delay, fourth allpass filter and another delay
+ float dry, link, outL1, outL2, outR1, outR2;
+
+ for (unsigned long i = 0; i < numSamples; i++) {
+ dry = *inLeft + *inRight;
+
+ // Implementation of 2-stage IIR single-pole low-pass filter
+ // found at the entrance of reverb processing on real devices
+ filterhist1 += (dry - filterhist1) * currentSettings->filtVal;
+ filterhist2 += (filterhist1 - filterhist2) * currentSettings->filtVal;
+
+ link = allpasses[0]->process(-filterhist2);
+ link = allpasses[1]->process(link);
+
+ // this implements a comb filter cross-linked with the fourth allpass filter
+ link += combhist * decayTime;
+ link = allpasses[2]->process(link);
+ link = delays[0]->process(link);
+ outL1 = link;
+ link = allpasses[3]->process(link);
+ link = delays[1]->process(link);
+ outR1 = link;
+ link = allpasses[4]->process(link);
+ link = delays[2]->process(link);
+ outL2 = link;
+ link = allpasses[5]->process(link);
+ link = delays[3]->process(link);
+ outR2 = link;
+ link = delays[4]->process(link);
+
+ // comb filter end point
+ combhist = combhist * currentSettings->damp1 + link * currentSettings->damp2;
+
+ *outLeft = (outL1 + outL2) * wetLevel;
+ *outRight = (outR1 + outR2) * wetLevel;
+
+ inLeft++;
+ inRight++;
+ outLeft++;
+ outRight++;
+ }
+}
diff --git a/audio/softsynth/mt32/AReverbModel.h b/audio/softsynth/mt32/AReverbModel.h
new file mode 100644
index 0000000000..3fae08c34c
--- /dev/null
+++ b/audio/softsynth/mt32/AReverbModel.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_A_REVERB_MODEL_H
+#define MT32EMU_A_REVERB_MODEL_H
+
+namespace MT32Emu {
+
+struct AReverbSettings {
+ const Bit32u *allpassSizes;
+ const Bit32u *delaySizes;
+ const float *decayTimes;
+ const float *wetLevels;
+ float filtVal;
+ float damp1;
+ float damp2;
+};
+
+class RingBuffer {
+protected:
+ float *buffer;
+ Bit32u size;
+ Bit32u index;
+public:
+ RingBuffer(Bit32u size);
+ virtual ~RingBuffer();
+ float next();
+ bool isEmpty();
+ void mute();
+};
+
+class AllpassFilter : public RingBuffer {
+public:
+ AllpassFilter(Bit32u size);
+ float process(float in);
+};
+
+class Delay : public RingBuffer {
+public:
+ Delay(Bit32u size);
+ float process(float in);
+};
+
+class AReverbModel : public ReverbModel {
+ AllpassFilter **allpasses;
+ Delay **delays;
+
+ const AReverbSettings *currentSettings;
+ float decayTime;
+ float wetLevel;
+ float filterhist1, filterhist2;
+ float combhist;
+ void mute();
+public:
+ AReverbModel(const AReverbSettings *newSettings);
+ ~AReverbModel();
+ void open(unsigned int sampleRate);
+ void close();
+ void setParameters(Bit8u time, Bit8u level);
+ void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
+ bool isActive() const;
+
+ static const AReverbSettings REVERB_MODE_0_SETTINGS;
+ static const AReverbSettings REVERB_MODE_1_SETTINGS;
+ static const AReverbSettings REVERB_MODE_2_SETTINGS;
+};
+
+// Default reverb settings for modes 0-2
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp
new file mode 100644
index 0000000000..89eebf0d79
--- /dev/null
+++ b/audio/softsynth/mt32/DelayReverb.cpp
@@ -0,0 +1,150 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cmath>
+//#include <cstring>
+#include "mt32emu.h"
+#include "DelayReverb.h"
+
+using namespace MT32Emu;
+
+
+// CONFIRMED: The values below are found via analysis of digital samples. Checked with all time and level combinations.
+// Obviously:
+// rightDelay = (leftDelay - 2) * 2 + 2
+// echoDelay = rightDelay - 1
+// Leaving these separate in case it's useful for work on other reverb modes...
+const Bit32u REVERB_TIMINGS[8][3]= {
+ // {leftDelay, rightDelay, feedbackDelay}
+ {402, 802, 801},
+ {626, 1250, 1249},
+ {962, 1922, 1921},
+ {1490, 2978, 2977},
+ {2258, 4514, 4513},
+ {3474, 6946, 6945},
+ {5282, 10562, 10561},
+ {8002, 16002, 16001}
+};
+
+const float REVERB_FADE[8] = {0.0f, -0.049400051f, -0.08220577f, -0.131861118f, -0.197344907f, -0.262956344f, -0.345162114f, -0.509508615f};
+const float REVERB_FEEDBACK67 = -0.629960524947437f; // = -EXP2F(-2 / 3)
+const float REVERB_FEEDBACK = -0.682034520443118f; // = -EXP2F(-53 / 96)
+const float LPF_VALUE = 0.594603558f; // = EXP2F(-0.75f)
+
+DelayReverb::DelayReverb() {
+ buf = NULL;
+ sampleRate = 0;
+ setParameters(0, 0);
+}
+
+DelayReverb::~DelayReverb() {
+ delete[] buf;
+}
+
+void DelayReverb::open(unsigned int newSampleRate) {
+ if (newSampleRate != sampleRate || buf == NULL) {
+ sampleRate = newSampleRate;
+
+ delete[] buf;
+
+ // If we ever need a speedup, set bufSize to EXP2F(ceil(log2(bufSize))) and use & instead of % to find buf indexes
+ bufSize = 16384 * sampleRate / 32000;
+ buf = new float[bufSize];
+
+ recalcParameters();
+
+ // mute buffer
+ bufIx = 0;
+ if (buf != NULL) {
+ for (unsigned int i = 0; i < bufSize; i++) {
+ buf[i] = 0.0f;
+ }
+ }
+ }
+ // FIXME: IIR filter value depends on sample rate as well
+}
+
+void DelayReverb::close() {
+ delete[] buf;
+ buf = NULL;
+}
+
+// This method will always trigger a flush of the buffer
+void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) {
+ time = newTime;
+ level = newLevel;
+ recalcParameters();
+}
+
+void DelayReverb::recalcParameters() {
+ // Number of samples between impulse and eventual appearance on the left channel
+ delayLeft = REVERB_TIMINGS[time][0] * sampleRate / 32000;
+ // Number of samples between impulse and eventual appearance on the right channel
+ delayRight = REVERB_TIMINGS[time][1] * sampleRate / 32000;
+ // Number of samples between a response and that response feeding back/echoing
+ delayFeedback = REVERB_TIMINGS[time][2] * sampleRate / 32000;
+
+ if (time < 6) {
+ feedback = REVERB_FEEDBACK;
+ } else {
+ feedback = REVERB_FEEDBACK67;
+ }
+
+ // Fading speed, i.e. amplitude ratio of neighbor responses
+ fade = REVERB_FADE[level];
+}
+
+void DelayReverb::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
+ if (buf == NULL) {
+ return;
+ }
+
+ for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) {
+ // The ring buffer write index moves backwards; reads are all done with positive offsets.
+ Bit32u bufIxPrev = (bufIx + 1) % bufSize;
+ Bit32u bufIxLeft = (bufIx + delayLeft) % bufSize;
+ Bit32u bufIxRight = (bufIx + delayRight) % bufSize;
+ Bit32u bufIxFeedback = (bufIx + delayFeedback) % bufSize;
+
+ // Attenuated input samples and feedback response are directly added to the current ring buffer location
+ float sample = fade * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback];
+
+ // Single-pole IIR filter found on real devices
+ buf[bufIx] = buf[bufIxPrev] + (sample - buf[bufIxPrev]) * LPF_VALUE;
+
+ outLeft[sampleIx] = buf[bufIxLeft];
+ outRight[sampleIx] = buf[bufIxRight];
+
+ bufIx = (bufSize + bufIx - 1) % bufSize;
+ }
+}
+
+bool DelayReverb::isActive() const {
+ // Quick hack: Return true iff all samples in the left buffer are the same and
+ // all samples in the right buffers are the same (within the sample output threshold).
+ if (buf == NULL) {
+ return false;
+ }
+ float last = buf[0] * 8192.0f;
+ for (unsigned int i = 1; i < bufSize; i++) {
+ float s = (buf[i] * 8192.0f);
+ if (fabs(s - last) > 1.0f) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/audio/softsynth/mt32/DelayReverb.h b/audio/softsynth/mt32/DelayReverb.h
new file mode 100644
index 0000000000..7c030fb839
--- /dev/null
+++ b/audio/softsynth/mt32/DelayReverb.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_DELAYREVERB_H
+#define MT32EMU_DELAYREVERB_H
+
+namespace MT32Emu {
+
+class DelayReverb : public ReverbModel {
+private:
+ Bit8u time;
+ Bit8u level;
+
+ unsigned int sampleRate;
+ Bit32u bufSize;
+ Bit32u bufIx;
+
+ float *buf;
+
+ Bit32u delayLeft;
+ Bit32u delayRight;
+ Bit32u delayFeedback;
+
+ float fade;
+ float feedback;
+
+ void recalcParameters();
+
+public:
+ DelayReverb();
+ ~DelayReverb();
+ void open(unsigned int sampleRate);
+ void close();
+ void setParameters(Bit8u time, Bit8u level);
+ void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
+ bool isActive() const;
+};
+}
+#endif
diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp
new file mode 100644
index 0000000000..c11fa859d8
--- /dev/null
+++ b/audio/softsynth/mt32/FreeverbModel.cpp
@@ -0,0 +1,78 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mt32emu.h"
+#include "FreeverbModel.h"
+
+#include "freeverb.h"
+
+using namespace MT32Emu;
+
+FreeverbModel::FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp) {
+ freeverb = NULL;
+ scaleTuning = useScaleTuning;
+ filtVal = useFiltVal;
+ wet = useWet;
+ room = useRoom;
+ damp = useDamp;
+}
+
+FreeverbModel::~FreeverbModel() {
+ delete freeverb;
+}
+
+void FreeverbModel::open(unsigned int /*sampleRate*/) {
+ // FIXME: scaleTuning must be multiplied by sample rate to 32000Hz ratio
+ // IIR filter values depend on sample rate as well
+ if (freeverb == NULL) {
+ freeverb = new revmodel(scaleTuning);
+ }
+ freeverb->mute();
+
+ // entrance Lowpass filter factor
+ freeverb->setfiltval(filtVal);
+
+ // decay speed of high frequencies in the wet signal
+ freeverb->setdamp(damp);
+}
+
+void FreeverbModel::close() {
+ delete freeverb;
+ freeverb = NULL;
+}
+
+void FreeverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
+ freeverb->process(inLeft, inRight, outLeft, outRight, numSamples);
+}
+
+void FreeverbModel::setParameters(Bit8u time, Bit8u level) {
+ // wet signal level
+ // FIXME: need to implement some sort of reverb level ramping
+ freeverb->setwet((float)level / 7.0f * wet);
+
+ // wet signal decay speed
+ static float roomTable[] = {
+ 0.25f, 0.37f, 0.54f, 0.71f, 0.78f, 0.86f, 0.93f, 1.00f,
+ -1.00f, -0.50f, 0.00f, 0.30f, 0.51f, 0.64f, 0.77f, 0.90f,
+ 0.50f, 0.57f, 0.70f, 0.77f, 0.85f, 0.93f, 0.96f, 1.01f};
+ freeverb->setroomsize(roomTable[8 * room + time]);
+}
+
+bool FreeverbModel::isActive() const {
+ // FIXME: Not bothering to do this properly since we'll be replacing Freeverb soon...
+ return false;
+}
diff --git a/audio/softsynth/mt32/FreeverbModel.h b/audio/softsynth/mt32/FreeverbModel.h
new file mode 100644
index 0000000000..925b2dbf96
--- /dev/null
+++ b/audio/softsynth/mt32/FreeverbModel.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_FREEVERB_MODEL_H
+#define MT32EMU_FREEVERB_MODEL_H
+
+class revmodel;
+
+namespace MT32Emu {
+
+class FreeverbModel : public ReverbModel {
+ revmodel *freeverb;
+ float scaleTuning;
+ float filtVal;
+ float wet;
+ Bit8u room;
+ float damp;
+public:
+ FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp);
+ ~FreeverbModel();
+ void open(unsigned int sampleRate);
+ void close();
+ void setParameters(Bit8u time, Bit8u level);
+ void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
+ bool isActive() const;
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp
new file mode 100644
index 0000000000..9f1f01c3c2
--- /dev/null
+++ b/audio/softsynth/mt32/LA32Ramp.cpp
@@ -0,0 +1,150 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+Some notes on this class:
+
+This emulates the LA-32's implementation of "ramps". A ramp in this context is a smooth transition from one value to another, handled entirely within the LA-32.
+The LA-32 provides this feature for amplitude and filter cutoff values.
+
+The 8095 starts ramps on the LA-32 by setting two values in memory-mapped registers:
+
+(1) The target value (between 0 and 255) for the ramp to end on. This is represented by the "target" argument to startRamp().
+(2) The speed at which that value should be approached. This is represented by the "increment" argument to startRamp().
+
+Once the ramp target value has been hit, the LA-32 raises an interrupt.
+
+Note that the starting point of the ramp is whatever internal value the LA-32 had when the registers were set. This is usually the end point of a previously completed ramp.
+
+Our handling of the "target" and "increment" values is based on sample analysis and a little guesswork.
+Here's what we're pretty confident about:
+ - The most significant bit of "increment" indicates the direction that the LA32's current internal value ("current" in our emulation) should change in.
+ Set means downward, clear means upward.
+ - The lower 7 bits of "increment" indicate how quickly "current" should be changed.
+ - If "increment" is 0, no change to "current" is made and no interrupt is raised. [SEMI-CONFIRMED by sample analysis]
+ - Otherwise, if the MSb is set:
+ - If "current" already corresponds to a value <= "target", "current" is set immediately to the equivalent of "target" and an interrupt is raised.
+ - Otherwise, "current" is gradually reduced (at a rate determined by the lower 7 bits of "increment"), and once it reaches the equivalent of "target" an interrupt is raised.
+ - Otherwise (the MSb is unset):
+ - If "current" already corresponds to a value >= "target", "current" is set immediately to the equivalent of "target" and an interrupt is raised.
+ - Otherwise, "current" is gradually increased (at a rate determined by the lower 7 bits of "increment"), and once it reaches the equivalent of "target" an interrupt is raised.
+
+We haven't fully explored:
+ - Values when ramping between levels (though this is probably correct).
+ - Transition timing (may not be 100% accurate, especially for very fast ramps).
+*/
+//#include <cmath>
+
+#include "mt32emu.h"
+#include "LA32Ramp.h"
+#include "mmath.h"
+
+namespace MT32Emu {
+
+// SEMI-CONFIRMED from sample analysis.
+const int TARGET_MULT = 0x40000;
+const unsigned int MAX_CURRENT = 0xFF * TARGET_MULT;
+
+// We simulate the delay in handling "target was reached" interrupts by waiting
+// this many samples before setting interruptRaised.
+// FIXME: This should vary with the sample rate, but doesn't.
+// SEMI-CONFIRMED: Since this involves asynchronous activity between the LA32
+// and the 8095, a good value is hard to pin down.
+// This one matches observed behaviour on a few digital captures I had handy,
+// and should be double-checked. We may also need a more sophisticated delay
+// scheme eventually.
+const int INTERRUPT_TIME = 7;
+
+LA32Ramp::LA32Ramp() :
+ current(0),
+ largeTarget(0),
+ largeIncrement(0),
+ interruptCountdown(0),
+ interruptRaised(false) {
+}
+
+void LA32Ramp::startRamp(Bit8u target, Bit8u increment) {
+ // CONFIRMED: From sample analysis, this appears to be very accurate.
+ // FIXME: We could use a table for this in future
+ if (increment == 0) {
+ largeIncrement = 0;
+ } else {
+ largeIncrement = (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f);
+ }
+ descending = (increment & 0x80) != 0;
+ if (descending) {
+ // CONFIRMED: From sample analysis, descending increments are slightly faster
+ largeIncrement++;
+ }
+
+ largeTarget = target * TARGET_MULT;
+ interruptCountdown = 0;
+ interruptRaised = false;
+}
+
+Bit32u LA32Ramp::nextValue() {
+ if (interruptCountdown > 0) {
+ if (--interruptCountdown == 0) {
+ interruptRaised = true;
+ }
+ } else if (largeIncrement != 0) {
+ // CONFIRMED from sample analysis: When increment is 0, the LA32 does *not* change the current value at all (and of course doesn't fire an interrupt).
+ if (descending) {
+ // Lowering current value
+ if (largeIncrement > current) {
+ current = largeTarget;
+ interruptCountdown = INTERRUPT_TIME;
+ } else {
+ current -= largeIncrement;
+ if (current <= largeTarget) {
+ current = largeTarget;
+ interruptCountdown = INTERRUPT_TIME;
+ }
+ }
+ } else {
+ // Raising current value
+ if (MAX_CURRENT - current < largeIncrement) {
+ current = largeTarget;
+ interruptCountdown = INTERRUPT_TIME;
+ } else {
+ current += largeIncrement;
+ if (current >= largeTarget) {
+ current = largeTarget;
+ interruptCountdown = INTERRUPT_TIME;
+ }
+ }
+ }
+ }
+ return current;
+}
+
+bool LA32Ramp::checkInterrupt() {
+ bool wasRaised = interruptRaised;
+ interruptRaised = false;
+ return wasRaised;
+}
+
+void LA32Ramp::reset() {
+ current = 0;
+ largeTarget = 0;
+ largeIncrement = 0;
+ descending = false;
+ interruptCountdown = 0;
+ interruptRaised = false;
+}
+
+}
diff --git a/audio/softsynth/mt32/LA32Ramp.h b/audio/softsynth/mt32/LA32Ramp.h
new file mode 100644
index 0000000000..ae937eb7e1
--- /dev/null
+++ b/audio/softsynth/mt32/LA32Ramp.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_LA32RAMP_H
+#define MT32EMU_LA32RAMP_H
+
+namespace MT32Emu {
+
+class LA32Ramp {
+private:
+ Bit32u current;
+ unsigned int largeTarget;
+ unsigned int largeIncrement;
+ bool descending;
+
+ int interruptCountdown;
+ bool interruptRaised;
+
+public:
+ LA32Ramp();
+ void startRamp(Bit8u target, Bit8u increment);
+ Bit32u nextValue();
+ bool checkInterrupt();
+ void reset();
+};
+
+}
+
+#endif /* TVA_H_ */
diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp
new file mode 100644
index 0000000000..8c646b2b49
--- /dev/null
+++ b/audio/softsynth/mt32/Part.cpp
@@ -0,0 +1,622 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cstdio>
+//#include <cstring>
+
+#include "mt32emu.h"
+#include "PartialManager.h"
+
+namespace MT32Emu {
+
+static const Bit8u PartialStruct[13] = {
+ 0, 0, 2, 2, 1, 3,
+ 3, 0, 3, 0, 2, 1, 3
+};
+
+static const Bit8u PartialMixStruct[13] = {
+ 0, 1, 0, 1, 1, 0,
+ 1, 3, 3, 2, 2, 2, 2
+};
+
+static const float floatKeyfollow[17] = {
+ -1.0f, -1.0f / 2.0f, -1.0f / 4.0f, 0.0f,
+ 1.0f / 8.0f, 1.0f / 4.0f, 3.0f / 8.0f, 1.0f / 2.0f, 5.0f / 8.0f, 3.0f / 4.0f, 7.0f / 8.0f, 1.0f,
+ 5.0f / 4.0f, 3.0f / 2.0f, 2.0f,
+ 1.0009765625f, 1.0048828125f
+};
+
+
+RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) {
+ strcpy(name, "Rhythm");
+ rhythmTemp = &synth->mt32ram.rhythmTemp[0];
+ refresh();
+}
+
+Part::Part(Synth *useSynth, unsigned int usePartNum) {
+ synth = useSynth;
+ partNum = usePartNum;
+ patchCache[0].dirty = true;
+ holdpedal = false;
+ patchTemp = &synth->mt32ram.patchTemp[partNum];
+ if (usePartNum == 8) {
+ // Nasty hack for rhythm
+ timbreTemp = NULL;
+ } else {
+ sprintf(name, "Part %d", partNum + 1);
+ timbreTemp = &synth->mt32ram.timbreTemp[partNum];
+ }
+ currentInstr[0] = 0;
+ currentInstr[10] = 0;
+ modulation = 0;
+ expression = 100;
+ pitchBend = 0;
+ activePartialCount = 0;
+ memset(patchCache, 0, sizeof(patchCache));
+ for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
+ freePolys.push_front(new Poly(this));
+ }
+}
+
+Part::~Part() {
+ while (!activePolys.empty()) {
+ delete activePolys.front();
+ activePolys.pop_front();
+ }
+ while (!freePolys.empty()) {
+ delete freePolys.front();
+ freePolys.pop_front();
+ }
+}
+
+void Part::setDataEntryMSB(unsigned char midiDataEntryMSB) {
+ if (nrpn) {
+ // The last RPN-related control change was for an NRPN,
+ // which the real synths don't support.
+ return;
+ }
+ if (rpn != 0) {
+ // The RPN has been set to something other than 0,
+ // which is the only RPN that these synths support
+ return;
+ }
+ patchTemp->patch.benderRange = midiDataEntryMSB > 24 ? 24 : midiDataEntryMSB;
+ updatePitchBenderRange();
+}
+
+void Part::setNRPN() {
+ nrpn = true;
+}
+
+void Part::setRPNLSB(unsigned char midiRPNLSB) {
+ nrpn = false;
+ rpn = (rpn & 0xFF00) | midiRPNLSB;
+}
+
+void Part::setRPNMSB(unsigned char midiRPNMSB) {
+ nrpn = false;
+ rpn = (rpn & 0x00FF) | (midiRPNMSB << 8);
+}
+
+void Part::setHoldPedal(bool pressed) {
+ if (holdpedal && !pressed) {
+ holdpedal = false;
+ stopPedalHold();
+ } else {
+ holdpedal = pressed;
+ }
+}
+
+Bit32s Part::getPitchBend() const {
+ return pitchBend;
+}
+
+void Part::setBend(unsigned int midiBend) {
+ // CONFIRMED:
+ pitchBend = (((signed)midiBend - 8192) * pitchBenderRange) >> 14; // PORTABILITY NOTE: Assumes arithmetic shift
+}
+
+Bit8u Part::getModulation() const {
+ return modulation;
+}
+
+void Part::setModulation(unsigned int midiModulation) {
+ modulation = (Bit8u)midiModulation;
+}
+
+void Part::resetAllControllers() {
+ modulation = 0;
+ expression = 100;
+ pitchBend = 0;
+ setHoldPedal(false);
+}
+
+void Part::reset() {
+ resetAllControllers();
+ allSoundOff();
+ rpn = 0xFFFF;
+}
+
+void RhythmPart::refresh() {
+ // (Re-)cache all the mapped timbres ahead of time
+ for (unsigned int drumNum = 0; drumNum < synth->controlROMMap->rhythmSettingsCount; drumNum++) {
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 127) { // 94 on MT-32
+ continue;
+ }
+ PatchCache *cache = drumCache[drumNum];
+ backupCacheToPartials(cache);
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ cache[t].dirty = true;
+ cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0;
+ }
+ }
+ updatePitchBenderRange();
+}
+
+void Part::refresh() {
+ backupCacheToPartials(patchCache);
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ patchCache[t].dirty = true;
+ patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
+ }
+ memcpy(currentInstr, timbreTemp->common.name, 10);
+ updatePitchBenderRange();
+}
+
+const char *Part::getCurrentInstr() const {
+ return &currentInstr[0];
+}
+
+void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
+ for (int m = 0; m < 85; m++) {
+ if (rhythmTemp[m].timbre == absTimbreNum - 128) {
+ drumCache[m][0].dirty = true;
+ }
+ }
+}
+
+void Part::refreshTimbre(unsigned int absTimbreNum) {
+ if (getAbsTimbreNum() == absTimbreNum) {
+ memcpy(currentInstr, timbreTemp->common.name, 10);
+ patchCache[0].dirty = true;
+ }
+}
+
+void Part::setPatch(const PatchParam *patch) {
+ patchTemp->patch = *patch;
+}
+
+void RhythmPart::setTimbre(TimbreParam * /*timbre*/) {
+ synth->printDebug("%s: Attempted to call setTimbre() - doesn't make sense for rhythm", name);
+}
+
+void Part::setTimbre(TimbreParam *timbre) {
+ *timbreTemp = *timbre;
+}
+
+unsigned int RhythmPart::getAbsTimbreNum() const {
+ synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm", name);
+ return 0;
+}
+
+unsigned int Part::getAbsTimbreNum() const {
+ return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
+}
+
+void RhythmPart::setProgram(unsigned int patchNum) {
+#if MT32EMU_MONITOR_MIDI > 0
+ synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum);
+#else
+ patchNum = 0; // Just to avoid unused variable warning
+#endif
+}
+
+void Part::setProgram(unsigned int patchNum) {
+ setPatch(&synth->mt32ram.patches[patchNum]);
+ holdpedal = false;
+ allSoundOff();
+ setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre);
+ refresh();
+}
+
+void Part::updatePitchBenderRange() {
+ pitchBenderRange = patchTemp->patch.benderRange * 683;
+}
+
+void Part::backupCacheToPartials(PatchCache cache[4]) {
+ // check if any partials are still playing with the old patch cache
+ // if so then duplicate the cached data from the part to the partial so that
+ // we can change the part's cache without affecting the partial.
+ // We delay this until now to avoid a copy operation with every note played
+ for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
+ (*polyIt)->backupCacheToPartials(cache);
+ }
+}
+
+void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
+ backupCacheToPartials(cache);
+ int partialCount = 0;
+ for (int t = 0; t < 4; t++) {
+ if (((timbre->common.partialMute >> t) & 0x1) == 1) {
+ cache[t].playPartial = true;
+ partialCount++;
+ } else {
+ cache[t].playPartial = false;
+ continue;
+ }
+
+ // Calculate and cache common parameters
+ cache[t].srcPartial = timbre->partial[t];
+
+ cache[t].pcm = timbre->partial[t].wg.pcmWave;
+
+ switch (t) {
+ case 0:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12];
+ cache[t].structurePosition = 0;
+ cache[t].structurePair = 1;
+ break;
+ case 1:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12];
+ cache[t].structurePosition = 1;
+ cache[t].structurePair = 0;
+ break;
+ case 2:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34];
+ cache[t].structurePosition = 0;
+ cache[t].structurePair = 3;
+ break;
+ case 3:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34];
+ cache[t].structurePosition = 1;
+ cache[t].structurePair = 2;
+ break;
+ default:
+ break;
+ }
+
+ cache[t].partialParam = &timbre->partial[t];
+
+ cache[t].waveform = timbre->partial[t].wg.waveform;
+ }
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ cache[t].dirty = false;
+ cache[t].partialCount = partialCount;
+ cache[t].sustain = (timbre->common.noSustain == 0);
+ }
+ //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform);
+
+#if MT32EMU_MONITOR_INSTRUMENTS > 0
+ synth->printDebug("%s (%s): Recached timbre", name, currentInstr);
+ for (int i = 0; i < 4; i++) {
+ synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmWave, timbre->partial[i].wg.waveform);
+ }
+#endif
+}
+
+const char *Part::getName() const {
+ return name;
+}
+
+void Part::setVolume(unsigned int midiVolume) {
+ // CONFIRMED: This calculation matches the table used in the control ROM
+ patchTemp->outputLevel = (Bit8u)(midiVolume * 100 / 127);
+ //synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume);
+}
+
+Bit8u Part::getVolume() const {
+ return patchTemp->outputLevel;
+}
+
+Bit8u Part::getExpression() const {
+ return expression;
+}
+
+void Part::setExpression(unsigned int midiExpression) {
+ // CONFIRMED: This calculation matches the table used in the control ROM
+ expression = (Bit8u)(midiExpression * 100 / 127);
+}
+
+void RhythmPart::setPan(unsigned int midiPan) {
+ // CONFIRMED: This does change patchTemp, but has no actual effect on playback.
+#if MT32EMU_MONITOR_MIDI > 0
+ synth->printDebug("%s: Pointlessly setting pan (%d) on rhythm part", name, midiPan);
+#endif
+ Part::setPan(midiPan);
+}
+
+void Part::setPan(unsigned int midiPan) {
+ // NOTE: Panning is inverted compared to GM.
+
+ // CM-32L: Divide by 8.5
+ patchTemp->panpot = (Bit8u)((midiPan << 3) / 68);
+ // FIXME: MT-32: Divide by 9
+ //patchTemp->panpot = (Bit8u)(midiPan / 9);
+
+ //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
+}
+
+/**
+ * Applies key shift to a MIDI key and converts it into an internal key value in the range 12-108.
+ */
+unsigned int Part::midiKeyToKey(unsigned int midiKey) {
+ int key = midiKey + patchTemp->patch.keyShift;
+ if (key < 36) {
+ // After keyShift is applied, key < 36, so move up by octaves
+ while (key < 36) {
+ key += 12;
+ }
+ } else if (key > 132) {
+ // After keyShift is applied, key > 132, so move down by octaves
+ while (key > 132) {
+ key -= 12;
+ }
+ }
+ key -= 24;
+ return key;
+}
+
+void RhythmPart::noteOn(unsigned int midiKey, unsigned int velocity) {
+ if (midiKey < 24 || midiKey > 108) { /*> 87 on MT-32)*/
+ synth->printDebug("%s: Attempted to play invalid key %d (velocity %d)", name, midiKey, velocity);
+ return;
+ }
+ unsigned int key = midiKey;
+ unsigned int drumNum = key - 24;
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 127) { // 94 on MT-32
+ synth->printDebug("%s: Attempted to play unmapped key %d (velocity %d)", name, midiKey, velocity);
+ return;
+ }
+ // CONFIRMED: Two special cases described by Mok
+ if (drumTimbreNum == 64 + 6) {
+ noteOff(0);
+ key = 1;
+ } else if (drumTimbreNum == 64 + 7) {
+ // This noteOff(0) is not performed on MT-32, only LAPC-I
+ noteOff(0);
+ key = 0;
+ }
+ int absTimbreNum = drumTimbreNum + 128;
+ TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre;
+ memcpy(currentInstr, timbre->common.name, 10);
+ if (drumCache[drumNum][0].dirty) {
+ cacheTimbre(drumCache[drumNum], timbre);
+ }
+#if MT32EMU_MONITOR_INSTRUMENTS > 0
+ synth->printDebug("%s (%s): Start poly (drum %d, timbre %d): midiKey %u, key %u, velo %u, mod %u, exp %u, bend %u", name, currentInstr, drumNum, absTimbreNum, midiKey, key, velocity, modulation, expression, pitchBend);
+#if MT32EMU_MONITOR_INSTRUMENTS > 1
+ // According to info from Mok, keyShift does not appear to affect anything on rhythm part on LAPC-I, but may do on MT-32 - needs investigation
+ synth->printDebug(" Patch: (timbreGroup %u), (timbreNum %u), (keyShift %u), fineTune %u, benderRange %u, assignMode %u, (reverbSwitch %u)", patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, patchTemp->patch.keyShift, patchTemp->patch.fineTune, patchTemp->patch.benderRange, patchTemp->patch.assignMode, patchTemp->patch.reverbSwitch);
+ synth->printDebug(" PatchTemp: outputLevel %u, (panpot %u)", patchTemp->outputLevel, patchTemp->panpot);
+ synth->printDebug(" RhythmTemp: timbre %u, outputLevel %u, panpot %u, reverbSwitch %u", rhythmTemp[drumNum].timbre, rhythmTemp[drumNum].outputLevel, rhythmTemp[drumNum].panpot, rhythmTemp[drumNum].reverbSwitch);
+#endif
+#endif
+ playPoly(drumCache[drumNum], &rhythmTemp[drumNum], midiKey, key, velocity);
+}
+
+void Part::noteOn(unsigned int midiKey, unsigned int velocity) {
+ unsigned int key = midiKeyToKey(midiKey);
+ if (patchCache[0].dirty) {
+ cacheTimbre(patchCache, timbreTemp);
+ }
+#if MT32EMU_MONITOR_INSTRUMENTS > 0
+ synth->printDebug("%s (%s): Start poly: midiKey %u, key %u, velo %u, mod %u, exp %u, bend %u", name, currentInstr, midiKey, key, velocity, modulation, expression, pitchBend);
+#if MT32EMU_MONITOR_INSTRUMENTS > 1
+ synth->printDebug(" Patch: timbreGroup %u, timbreNum %u, keyShift %u, fineTune %u, benderRange %u, assignMode %u, reverbSwitch %u", patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, patchTemp->patch.keyShift, patchTemp->patch.fineTune, patchTemp->patch.benderRange, patchTemp->patch.assignMode, patchTemp->patch.reverbSwitch);
+ synth->printDebug(" PatchTemp: outputLevel %u, panpot %u", patchTemp->outputLevel, patchTemp->panpot);
+#endif
+#endif
+ playPoly(patchCache, NULL, midiKey, key, velocity);
+}
+
+void Part::abortPoly(Poly *poly) {
+ if (poly->startAbort()) {
+ while (poly->isActive()) {
+ if (!synth->prerender()) {
+ synth->printDebug("%s (%s): Ran out of prerender space to abort poly gracefully", name, currentInstr);
+ poly->terminate();
+ break;
+ }
+ }
+ }
+}
+
+bool Part::abortFirstPoly(unsigned int key) {
+ for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
+ Poly *poly = *polyIt;
+ if (poly->getKey() == key) {
+ abortPoly(poly);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Part::abortFirstPoly(PolyState polyState) {
+ for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
+ Poly *poly = *polyIt;
+ if (poly->getState() == polyState) {
+ abortPoly(poly);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Part::abortFirstPolyPreferHeld() {
+ if (abortFirstPoly(POLY_Held)) {
+ return true;
+ }
+ return abortFirstPoly();
+}
+
+bool Part::abortFirstPoly() {
+ if (activePolys.empty()) {
+ return false;
+ }
+ abortPoly(activePolys.front());
+ return true;
+}
+
+void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhythmTemp, unsigned int midiKey, unsigned int key, unsigned int velocity) {
+ // CONFIRMED: Even in single-assign mode, we don't abort playing polys if the timbre to play is completely muted.
+ unsigned int needPartials = cache[0].partialCount;
+ if (needPartials == 0) {
+ synth->printDebug("%s (%s): Completely muted instrument", name, currentInstr);
+ return;
+ }
+
+ if ((patchTemp->patch.assignMode & 2) == 0) {
+ // Single-assign mode
+ abortFirstPoly(key);
+ }
+
+ if (!synth->partialManager->freePartials(needPartials, partNum)) {
+#if MT32EMU_MONITOR_PARTIALS > 0
+ synth->printDebug("%s (%s): Insufficient free partials to play key %d (velocity %d); needed=%d, free=%d, assignMode=%d", name, currentInstr, midiKey, velocity, needPartials, synth->partialManager->getFreePartialCount(), patchTemp->patch.assignMode);
+ synth->printPartialUsage();
+#endif
+ return;
+ }
+
+ if (freePolys.empty()) {
+ synth->printDebug("%s (%s): No free poly to play key %d (velocity %d)", name, currentInstr, midiKey, velocity);
+ return;
+ }
+ Poly *poly = freePolys.front();
+ freePolys.pop_front();
+ if (patchTemp->patch.assignMode & 1) {
+ // Priority to data first received
+ activePolys.push_front(poly);
+ } else {
+ activePolys.push_back(poly);
+ }
+
+ Partial *partials[4];
+ for (int x = 0; x < 4; x++) {
+ if (cache[x].playPartial) {
+ partials[x] = synth->partialManager->allocPartial(partNum);
+ activePartialCount++;
+ } else {
+ partials[x] = NULL;
+ }
+ }
+ poly->reset(key, velocity, cache[0].sustain, partials);
+
+ for (int x = 0; x < 4; x++) {
+ if (partials[x] != NULL) {
+#if MT32EMU_MONITOR_PARTIALS > 2
+ synth->printDebug("%s (%s): Allocated partial %d", name, currentInstr, partials[x]->debugGetPartialNum());
+#endif
+ partials[x]->startPartial(this, poly, &cache[x], rhythmTemp, partials[cache[x].structurePair]);
+ }
+ }
+#if MT32EMU_MONITOR_PARTIALS > 1
+ synth->printPartialUsage();
+#endif
+}
+
+void Part::allNotesOff() {
+ // The MIDI specification states - and Mok confirms - that all notes off (0x7B)
+ // should treat the hold pedal as usual.
+ for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
+ Poly *poly = *polyIt;
+ // FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed
+ // applies to AllNotesOff.
+ poly->noteOff(holdpedal);
+ }
+}
+
+void Part::allSoundOff() {
+ // MIDI "All sound off" (0x78) should release notes immediately regardless of the hold pedal.
+ // This controller is not actually implemented by the synths, though (according to the docs and Mok) -
+ // we're only using this method internally.
+ for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
+ Poly *poly = *polyIt;
+ poly->startDecay();
+ }
+}
+
+void Part::stopPedalHold() {
+ for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
+ Poly *poly = *polyIt;
+ poly->stopPedalHold();
+ }
+}
+
+void RhythmPart::noteOff(unsigned int midiKey) {
+ stopNote(midiKey);
+}
+
+void Part::noteOff(unsigned int midiKey) {
+ stopNote(midiKeyToKey(midiKey));
+}
+
+void Part::stopNote(unsigned int key) {
+#if MT32EMU_MONITOR_INSTRUMENTS > 0
+ synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key);
+#endif
+
+ for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
+ Poly *poly = *polyIt;
+ // Generally, non-sustaining instruments ignore note off. They die away eventually anyway.
+ // Key 0 (only used by special cases on rhythm part) reacts to note off even if non-sustaining or pedal held.
+ if (poly->getKey() == key && (poly->canSustain() || key == 0)) {
+ if (poly->noteOff(holdpedal && key != 0)) {
+ break;
+ }
+ }
+ }
+}
+
+const MemParams::PatchTemp *Part::getPatchTemp() const {
+ return patchTemp;
+}
+
+unsigned int Part::getActivePartialCount() const {
+ return activePartialCount;
+}
+
+unsigned int Part::getActiveNonReleasingPartialCount() const {
+ unsigned int activeNonReleasingPartialCount = 0;
+ for (Common::List<Poly *>::const_iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
+ Poly *poly = *polyIt;
+ if (poly->getState() != POLY_Releasing) {
+ activeNonReleasingPartialCount += poly->getActivePartialCount();
+ }
+ }
+ return activeNonReleasingPartialCount;
+}
+
+void Part::partialDeactivated(Poly *poly) {
+ activePartialCount--;
+ if (!poly->isActive()) {
+ activePolys.remove(poly);
+ freePolys.push_front(poly);
+ }
+}
+
+}
diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h
new file mode 100644
index 0000000000..befb05c532
--- /dev/null
+++ b/audio/softsynth/mt32/Part.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_PART_H
+#define MT32EMU_PART_H
+
+#include <common/list.h>
+
+namespace MT32Emu {
+
+class PartialManager;
+class Synth;
+
+class Part {
+private:
+ // Direct pointer to sysex-addressable memory dedicated to this part (valid for parts 1-8, NULL for rhythm)
+ TimbreParam *timbreTemp;
+
+ // 0=Part 1, .. 7=Part 8, 8=Rhythm
+ unsigned int partNum;
+
+ bool holdpedal;
+
+ unsigned int activePartialCount;
+ PatchCache patchCache[4];
+ Common::List<Poly*> freePolys;
+ Common::List<Poly*> activePolys;
+
+ void setPatch(const PatchParam *patch);
+ unsigned int midiKeyToKey(unsigned int midiKey);
+
+ void abortPoly(Poly *poly);
+ bool abortFirstPoly(unsigned int key);
+
+protected:
+ Synth *synth;
+ // Direct pointer into sysex-addressable memory
+ MemParams::PatchTemp *patchTemp;
+ char name[8]; // "Part 1".."Part 8", "Rhythm"
+ char currentInstr[11];
+ Bit8u modulation;
+ Bit8u expression;
+ Bit32s pitchBend;
+ bool nrpn;
+ Bit16u rpn;
+ Bit16u pitchBenderRange; // (patchTemp->patch.benderRange * 683) at the time of the last MIDI program change or MIDI data entry.
+
+ void backupCacheToPartials(PatchCache cache[4]);
+ void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre);
+ void playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhythmTemp, unsigned int midiKey, unsigned int key, unsigned int velocity);
+ void stopNote(unsigned int key);
+ const char *getName() const;
+
+public:
+ Part(Synth *synth, unsigned int usePartNum);
+ virtual ~Part();
+ void reset();
+ void setDataEntryMSB(unsigned char midiDataEntryMSB);
+ void setNRPN();
+ void setRPNLSB(unsigned char midiRPNLSB);
+ void setRPNMSB(unsigned char midiRPNMSB);
+ void resetAllControllers();
+ virtual void noteOn(unsigned int midiKey, unsigned int velocity);
+ virtual void noteOff(unsigned int midiKey);
+ void allNotesOff();
+ void allSoundOff();
+ Bit8u getVolume() const; // Internal volume, 0-100, exposed for use by ExternalInterface
+ void setVolume(unsigned int midiVolume);
+ Bit8u getModulation() const;
+ void setModulation(unsigned int midiModulation);
+ Bit8u getExpression() const;
+ void setExpression(unsigned int midiExpression);
+ virtual void setPan(unsigned int midiPan);
+ Bit32s getPitchBend() const;
+ void setBend(unsigned int midiBend);
+ virtual void setProgram(unsigned int midiProgram);
+ void setHoldPedal(bool pedalval);
+ void stopPedalHold();
+ void updatePitchBenderRange();
+ virtual void refresh();
+ virtual void refreshTimbre(unsigned int absTimbreNum);
+ virtual void setTimbre(TimbreParam *timbre);
+ virtual unsigned int getAbsTimbreNum() const;
+ const char *getCurrentInstr() const;
+ unsigned int getActivePartialCount() const;
+ unsigned int getActiveNonReleasingPartialCount() const;
+
+ const MemParams::PatchTemp *getPatchTemp() const;
+
+ // This should only be called by Poly
+ void partialDeactivated(Poly *poly);
+
+ // These are rather specialised, and should probably only be used by PartialManager
+ bool abortFirstPoly(PolyState polyState);
+ // Abort the first poly in PolyState_HELD, or if none exists, the first active poly in any state.
+ bool abortFirstPolyPreferHeld();
+ bool abortFirstPoly();
+};
+
+class RhythmPart: public Part {
+ // Pointer to the area of the MT-32's memory dedicated to rhythm
+ const MemParams::RhythmTemp *rhythmTemp;
+
+ // This caches the timbres/settings in use by the rhythm part
+ PatchCache drumCache[85][4];
+public:
+ RhythmPart(Synth *synth, unsigned int usePartNum);
+ void refresh();
+ void refreshTimbre(unsigned int timbreNum);
+ void setTimbre(TimbreParam *timbre);
+ void noteOn(unsigned int key, unsigned int velocity);
+ void noteOff(unsigned int midiKey);
+ unsigned int getAbsTimbreNum() const;
+ void setPan(unsigned int midiPan);
+ void setProgram(unsigned int patchNum);
+};
+
+}
+#endif
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp
new file mode 100644
index 0000000000..03bec560b8
--- /dev/null
+++ b/audio/softsynth/mt32/Partial.cpp
@@ -0,0 +1,557 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cmath>
+//#include <cstdlib>
+//#include <cstring>
+
+#include "mt32emu.h"
+#include "mmath.h"
+
+using namespace MT32Emu;
+
+#ifdef INACCURATE_SMOOTH_PAN
+// Mok wanted an option for smoother panning, and we love Mok.
+static const float PAN_NUMERATOR_NORMAL[] = {0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 5.5f, 6.0f, 6.5f, 7.0f};
+#else
+// CONFIRMED by Mok: These NUMERATOR values (as bytes, not floats, obviously) are sent exactly like this to the LA32.
+static const float PAN_NUMERATOR_NORMAL[] = {0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f, 4.0f, 4.0f, 5.0f, 5.0f, 6.0f, 6.0f, 7.0f};
+#endif
+static const float PAN_NUMERATOR_MASTER[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f};
+static const float PAN_NUMERATOR_SLAVE[] = {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f};
+
+Partial::Partial(Synth *useSynth, int useDebugPartialNum) :
+ synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0), tva(new TVA(this, &ampRamp)), tvp(new TVP(this)), tvf(new TVF(this, &cutoffModifierRamp)) {
+ ownerPart = -1;
+ poly = NULL;
+ pair = NULL;
+}
+
+Partial::~Partial() {
+ delete tva;
+ delete tvp;
+ delete tvf;
+}
+
+// Only used for debugging purposes
+int Partial::debugGetPartialNum() const {
+ return debugPartialNum;
+}
+
+// Only used for debugging purposes
+unsigned long Partial::debugGetSampleNum() const {
+ return sampleNum;
+}
+
+int Partial::getOwnerPart() const {
+ return ownerPart;
+}
+
+bool Partial::isActive() const {
+ return ownerPart > -1;
+}
+
+const Poly *Partial::getPoly() const {
+ return poly;
+}
+
+void Partial::activate(int part) {
+ // This just marks the partial as being assigned to a part
+ ownerPart = part;
+}
+
+void Partial::deactivate() {
+ if (!isActive()) {
+ return;
+ }
+ ownerPart = -1;
+ if (poly != NULL) {
+ poly->partialDeactivated(this);
+ if (pair != NULL) {
+ pair->pair = NULL;
+ }
+ }
+#if MT32EMU_MONITOR_PARTIALS > 2
+ synth->printDebug("[+%lu] [Partial %d] Deactivated", sampleNum, debugPartialNum);
+ synth->printPartialUsage(sampleNum);
+#endif
+}
+
+// DEPRECATED: This should probably go away eventually, it's currently only used as a kludge to protect our old assumptions that
+// rhythm part notes were always played as key MIDDLEC.
+int Partial::getKey() const {
+ if (poly == NULL) {
+ return -1;
+ } else if (ownerPart == 8) {
+ // FIXME: Hack, should go away after new pitch stuff is committed (and possibly some TVF changes)
+ return MIDDLEC;
+ } else {
+ return poly->getKey();
+ }
+}
+
+void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *usePatchCache, const MemParams::RhythmTemp *rhythmTemp, Partial *pairPartial) {
+ if (usePoly == NULL || usePatchCache == NULL) {
+ synth->printDebug("[Partial %d] *** Error: Starting partial for owner %d, usePoly=%s, usePatchCache=%s", debugPartialNum, ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", usePatchCache == NULL ? "*** NULL ***" : "OK");
+ return;
+ }
+ patchCache = usePatchCache;
+ poly = usePoly;
+ mixType = patchCache->structureMix;
+ structurePosition = patchCache->structurePosition;
+
+ Bit8u panSetting = rhythmTemp != NULL ? rhythmTemp->panpot : part->getPatchTemp()->panpot;
+ float panVal;
+ if (mixType == 3) {
+ if (structurePosition == 0) {
+ panVal = PAN_NUMERATOR_MASTER[panSetting];
+ } else {
+ panVal = PAN_NUMERATOR_SLAVE[panSetting];
+ }
+ // Do a normal mix independent of any pair partial.
+ mixType = 0;
+ pairPartial = NULL;
+ } else {
+ panVal = PAN_NUMERATOR_NORMAL[panSetting];
+ }
+
+ // FIXME: Sample analysis suggests that the use of panVal is linear, but there are some some quirks that still need to be resolved.
+ stereoVolume.leftVol = panVal / 7.0f;
+ stereoVolume.rightVol = 1.0f - stereoVolume.leftVol;
+
+ if (patchCache->PCMPartial) {
+ pcmNum = patchCache->pcm;
+ if (synth->controlROMMap->pcmCount > 128) {
+ // CM-32L, etc. support two "banks" of PCMs, selectable by waveform type parameter.
+ if (patchCache->waveform > 1) {
+ pcmNum += 128;
+ }
+ }
+ pcmWave = &synth->pcmWaves[pcmNum];
+ } else {
+ pcmWave = NULL;
+ wavePos = 0.0f;
+ lastFreq = 0.0;
+ }
+
+ // CONFIRMED: pulseWidthVal calculation is based on information from Mok
+ pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + synth->tables.pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth];
+ if (pulseWidthVal < 0) {
+ pulseWidthVal = 0;
+ } else if (pulseWidthVal > 255) {
+ pulseWidthVal = 255;
+ }
+
+ pcmPosition = 0.0f;
+ pair = pairPartial;
+ alreadyOutputed = false;
+ tva->reset(part, patchCache->partialParam, rhythmTemp);
+ tvp->reset(part, patchCache->partialParam);
+ tvf->reset(patchCache->partialParam, tvp->getBasePitch());
+}
+
+float Partial::getPCMSample(unsigned int position) {
+ if (position >= pcmWave->len) {
+ if (!pcmWave->loop) {
+ return 0;
+ }
+ position = position % pcmWave->len;
+ }
+ return synth->pcmROMData[pcmWave->addr + position];
+}
+
+unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) {
+ if (!isActive() || alreadyOutputed) {
+ return 0;
+ }
+ if (poly == NULL) {
+ synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::generateSamples()!", debugPartialNum);
+ return 0;
+ }
+
+ alreadyOutputed = true;
+
+ // Generate samples
+
+ for (sampleNum = 0; sampleNum < length; sampleNum++) {
+ float sample = 0;
+ Bit32u ampRampVal = ampRamp.nextValue();
+ if (ampRamp.checkInterrupt()) {
+ tva->handleInterrupt();
+ }
+ if (!tva->isPlaying()) {
+ deactivate();
+ break;
+ }
+ // SEMI-CONFIRMED: From sample analysis:
+ // (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.
+ // This gives results within +/- 2 at the output (before any DAC bitshifting)
+ // when sustaining at levels 156 - 255 with no modifiers.
+ // (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255.
+ // This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces
+ // positive amps, so negative still needs to be explored, as well as lower levels.
+ //
+ // Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing.
+ float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f);
+
+ Bit16u pitch = tvp->nextPitch();
+ float freq = synth->tables.pitchToFreq[pitch];
+
+ if (patchCache->PCMPartial) {
+ // Render PCM waveform
+ int len = pcmWave->len;
+ int intPCMPosition = (int)pcmPosition;
+ if (intPCMPosition >= len && !pcmWave->loop) {
+ // We're now past the end of a non-looping PCM waveform so it's time to die.
+ deactivate();
+ break;
+ }
+ Bit32u pcmAddr = pcmWave->addr;
+ float positionDelta = freq * 2048.0f / synth->myProp.sampleRate;
+
+ // Linear interpolation
+ float firstSample = synth->pcmROMData[pcmAddr + intPCMPosition];
+ float nextSample = getPCMSample(intPCMPosition + 1);
+ sample = firstSample + (nextSample - firstSample) * (pcmPosition - intPCMPosition);
+
+ float newPCMPosition = pcmPosition + positionDelta;
+ if (pcmWave->loop) {
+ newPCMPosition = fmod(newPCMPosition, (float)pcmWave->len);
+ }
+ pcmPosition = newPCMPosition;
+ } else {
+ // Render synthesised waveform
+ wavePos *= lastFreq / freq;
+ lastFreq = freq;
+
+ Bit32u cutoffModifierRampVal = cutoffModifierRamp.nextValue();
+ if (cutoffModifierRamp.checkInterrupt()) {
+ tvf->handleInterrupt();
+ }
+ float cutoffModifier = cutoffModifierRampVal / 262144.0f;
+
+ // res corresponds to a value set in an LA32 register
+ Bit8u res = patchCache->srcPartial.tvf.resonance + 1;
+
+ // EXP2F(1.0f - (32 - res) / 4.0f);
+ float resAmp = synth->tables.resAmpMax[res];
+
+ // The cutoffModifier may not be supposed to be directly added to the cutoff -
+ // it may for example need to be multiplied in some way.
+ // The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4).
+ // More research is needed to be sure that this is correct, however.
+ float cutoffVal = tvf->getBaseCutoff() + cutoffModifier;
+ if (cutoffVal > 240.0f) {
+ cutoffVal = 240.0f;
+ }
+
+ // Wave length in samples
+ float waveLen = synth->myProp.sampleRate / freq;
+
+ // Init cosineLen
+ float cosineLen = 0.5f * waveLen;
+ if (cutoffVal > 128.0f) {
+#if MT32EMU_ACCURATE_WG == 1
+ cosineLen *= EXP2F((cutoffVal - 128.0f) / -16.0f); // found from sample analysis
+#else
+ cosineLen *= synth->tables.cutoffToCosineLen[Bit32u((cutoffVal - 128.0f) * 8.0f)];
+#endif
+ }
+
+ // Start playing in center of first cosine segment
+ // relWavePos is shifted by a half of cosineLen
+ float relWavePos = wavePos + 0.5f * cosineLen;
+ if (relWavePos > waveLen) {
+ relWavePos -= waveLen;
+ }
+
+ float pulseLen = 0.5f;
+ if (pulseWidthVal > 128) {
+ pulseLen += synth->tables.pulseLenFactor[pulseWidthVal - 128];
+ }
+ pulseLen *= waveLen;
+
+ float lLen = pulseLen - cosineLen;
+
+ // Ignore pulsewidths too high for given freq
+ if (lLen < 0.0f) {
+ lLen = 0.0f;
+ }
+
+ // Ignore pulsewidths too high for given freq and cutoff
+ float hLen = waveLen - lLen - 2 * cosineLen;
+ if (hLen < 0.0f) {
+ hLen = 0.0f;
+ }
+
+ // Correct resAmp for cutoff in range 50..66
+ if ((cutoffVal >= 128.0f) && (cutoffVal < 144.0f)) {
+#if MT32EMU_ACCURATE_WG == 1
+ resAmp *= sinf(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f);
+#else
+ resAmp *= synth->tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))];
+#endif
+ }
+
+ // Produce filtered square wave with 2 cosine waves on slopes
+
+ // 1st cosine segment
+ if (relWavePos < cosineLen) {
+#if MT32EMU_ACCURATE_WG == 1
+ sample = -cosf(FLOAT_PI * relWavePos / cosineLen);
+#else
+ sample = -synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024];
+#endif
+ } else
+
+ // high linear segment
+ if (relWavePos < (cosineLen + hLen)) {
+ sample = 1.f;
+ } else
+
+ // 2nd cosine segment
+ if (relWavePos < (2 * cosineLen + hLen)) {
+#if MT32EMU_ACCURATE_WG == 1
+ sample = cosf(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen);
+#else
+ sample = synth->tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024];
+#endif
+ } else {
+
+ // low linear segment
+ sample = -1.f;
+ }
+
+ if (cutoffVal < 128.0f) {
+
+ // Attenuate samples below cutoff 50
+ // Found by sample analysis
+#if MT32EMU_ACCURATE_WG == 1
+ sample *= EXP2F(-0.125f * (128.0f - cutoffVal));
+#else
+ sample *= synth->tables.cutoffToFilterAmp[Bit32u(cutoffVal * 8.0f)];
+#endif
+ } else {
+
+ // Add resonance sine. Effective for cutoff > 50 only
+ float resSample = 1.0f;
+
+ // Now relWavePos counts from the middle of first cosine
+ relWavePos = wavePos;
+
+ // negative segments
+ if (!(relWavePos < (cosineLen + hLen))) {
+ resSample = -resSample;
+ relWavePos -= cosineLen + hLen;
+ }
+
+ // Resonance sine WG
+#if MT32EMU_ACCURATE_WG == 1
+ resSample *= sinf(FLOAT_PI * relWavePos / cosineLen);
+#else
+ resSample *= synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095];
+#endif
+
+ // Resonance sine amp
+ float resAmpFade = EXP2F(-synth->tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen)); // seems to be exact
+
+ // Now relWavePos set negative to the left from center of any cosine
+ relWavePos = wavePos;
+
+ // negative segment
+ if (!(wavePos < (waveLen - 0.5f * cosineLen))) {
+ relWavePos -= waveLen;
+ } else
+
+ // positive segment
+ if (!(wavePos < (hLen + 0.5f * cosineLen))) {
+ relWavePos -= cosineLen + hLen;
+ }
+
+ // Fading to zero while within cosine segments to avoid jumps in the wave
+ // Sample analysis suggests that this window is very close to cosine
+ if (relWavePos < 0.5f * cosineLen) {
+#if MT32EMU_ACCURATE_WG == 1
+ resAmpFade *= 0.5f * (1.0f - cosf(FLOAT_PI * relWavePos / (0.5f * cosineLen)));
+#else
+ resAmpFade *= 0.5f * (1.0f + synth->tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]);
+#endif
+ }
+
+ sample += resSample * resAmp * resAmpFade;
+ }
+
+ // sawtooth waves
+ if ((patchCache->waveform & 1) != 0) {
+#if MT32EMU_ACCURATE_WG == 1
+ sample *= cosf(FLOAT_2PI * wavePos / waveLen);
+#else
+ sample *= synth->tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024];
+#endif
+ }
+
+ wavePos++;
+
+ // wavePos isn't supposed to be > waveLen
+ if (wavePos > waveLen) {
+ wavePos -= waveLen;
+ }
+ }
+
+ // Multiply sample with current TVA value
+ sample *= amp;
+ *partialBuf++ = sample;
+ }
+ unsigned long renderedSamples = sampleNum;
+ sampleNum = 0;
+ return renderedSamples;
+}
+
+float *Partial::mixBuffersRingMix(float *buf1, float *buf2, unsigned long len) {
+ if (buf1 == NULL) {
+ return NULL;
+ }
+ if (buf2 == NULL) {
+ return buf1;
+ }
+
+ while (len--) {
+ // FIXME: At this point we have no idea whether this is remotely correct...
+ *buf1 = *buf1 * *buf2 + *buf1;
+ buf1++;
+ buf2++;
+ }
+ return buf1;
+}
+
+float *Partial::mixBuffersRing(float *buf1, float *buf2, unsigned long len) {
+ if (buf1 == NULL) {
+ return NULL;
+ }
+ if (buf2 == NULL) {
+ return NULL;
+ }
+
+ while (len--) {
+ // FIXME: At this point we have no idea whether this is remotely correct...
+ *buf1 = *buf1 * *buf2;
+ buf1++;
+ buf2++;
+ }
+ return buf1;
+}
+
+bool Partial::hasRingModulatingSlave() const {
+ return pair != NULL && structurePosition == 0 && (mixType == 1 || mixType == 2);
+}
+
+bool Partial::isRingModulatingSlave() const {
+ return pair != NULL && structurePosition == 1 && (mixType == 1 || mixType == 2);
+}
+
+bool Partial::isPCM() const {
+ return pcmWave != NULL;
+}
+
+const ControlROMPCMStruct *Partial::getControlROMPCMStruct() const {
+ if (pcmWave != NULL) {
+ return pcmWave->controlROMPCMStruct;
+ }
+ return NULL;
+}
+
+Synth *Partial::getSynth() const {
+ return synth;
+}
+
+bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long length) {
+ if (!isActive() || alreadyOutputed || isRingModulatingSlave()) {
+ return false;
+ }
+ if (poly == NULL) {
+ synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum);
+ return false;
+ }
+
+ float *partialBuf = &myBuffer[0];
+ unsigned long numGenerated = generateSamples(partialBuf, length);
+ if (mixType == 1 || mixType == 2) {
+ float *pairBuf;
+ unsigned long pairNumGenerated;
+ if (pair == NULL) {
+ pairBuf = NULL;
+ pairNumGenerated = 0;
+ } else {
+ pairBuf = &pair->myBuffer[0];
+ pairNumGenerated = pair->generateSamples(pairBuf, numGenerated);
+ // pair will have been set to NULL if it deactivated within generateSamples()
+ if (pair != NULL) {
+ if (!isActive()) {
+ pair->deactivate();
+ pair = NULL;
+ } else if (!pair->isActive()) {
+ pair = NULL;
+ }
+ }
+ }
+ if (pairNumGenerated > 0) {
+ if (mixType == 1) {
+ mixBuffersRingMix(partialBuf, pairBuf, pairNumGenerated);
+ } else {
+ mixBuffersRing(partialBuf, pairBuf, pairNumGenerated);
+ }
+ }
+ if (numGenerated > pairNumGenerated) {
+ if (mixType == 1) {
+ mixBuffersRingMix(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated);
+ } else {
+ mixBuffersRing(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated);
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < numGenerated; i++) {
+ *leftBuf++ = partialBuf[i] * stereoVolume.leftVol;
+ }
+ for (unsigned int i = 0; i < numGenerated; i++) {
+ *rightBuf++ = partialBuf[i] * stereoVolume.rightVol;
+ }
+ while (numGenerated < length) {
+ *leftBuf++ = 0.0f;
+ *rightBuf++ = 0.0f;
+ numGenerated++;
+ }
+ return true;
+}
+
+bool Partial::shouldReverb() {
+ if (!isActive()) {
+ return false;
+ }
+ return patchCache->reverb;
+}
+
+void Partial::startAbort() {
+ // This is called when the partial manager needs to terminate partials for re-use by a new Poly.
+ tva->startAbort();
+}
+
+void Partial::startDecayAll() {
+ tva->startDecay();
+ tvp->startDecay();
+ tvf->startDecay();
+}
diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h
new file mode 100644
index 0000000000..95218c858c
--- /dev/null
+++ b/audio/softsynth/mt32/Partial.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_PARTIAL_H
+#define MT32EMU_PARTIAL_H
+
+namespace MT32Emu {
+
+class Synth;
+class Part;
+class TVA;
+struct ControlROMPCMStruct;
+
+struct StereoVolume {
+ float leftVol;
+ float rightVol;
+};
+
+// A partial represents one of up to four waveform generators currently playing within a poly.
+class Partial {
+private:
+ Synth *synth;
+ const int debugPartialNum; // Only used for debugging
+ // Number of the sample currently being rendered by generateSamples(), or 0 if no run is in progress
+ // This is only kept available for debugging purposes.
+ unsigned long sampleNum;
+
+ int ownerPart; // -1 if unassigned
+ int mixType;
+ int structurePosition; // 0 or 1 of a structure pair
+ StereoVolume stereoVolume;
+
+ // Distance in (possibly fractional) samples from the start of the current pulse
+ float wavePos;
+
+ float lastFreq;
+
+ float myBuffer[MAX_SAMPLES_PER_RUN];
+
+ // Only used for PCM partials
+ int pcmNum;
+ // FIXME: Give this a better name (e.g. pcmWaveInfo)
+ PCMWaveEntry *pcmWave;
+
+ // Final pulse width value, with velfollow applied, matching what is sent to the LA32.
+ // Range: 0-255
+ int pulseWidthVal;
+
+ float pcmPosition;
+
+ Poly *poly;
+
+ LA32Ramp ampRamp;
+ LA32Ramp cutoffModifierRamp;
+
+ float *mixBuffersRingMix(float *buf1, float *buf2, unsigned long len);
+ float *mixBuffersRing(float *buf1, float *buf2, unsigned long len);
+
+ float getPCMSample(unsigned int position);
+
+public:
+ const PatchCache *patchCache;
+ TVA *tva;
+ TVP *tvp;
+ TVF *tvf;
+
+ PatchCache cachebackup;
+
+ Partial *pair;
+ bool alreadyOutputed;
+
+ Partial(Synth *synth, int debugPartialNum);
+ ~Partial();
+
+ int debugGetPartialNum() const;
+ unsigned long debugGetSampleNum() const;
+
+ int getOwnerPart() const;
+ int getKey() const;
+ const Poly *getPoly() const;
+ bool isActive() const;
+ void activate(int part);
+ void deactivate(void);
+ void startPartial(const Part *part, Poly *usePoly, const PatchCache *useCache, const MemParams::RhythmTemp *rhythmTemp, Partial *pairPartial);
+ void startAbort();
+ void startDecayAll();
+ bool shouldReverb();
+ bool hasRingModulatingSlave() const;
+ bool isRingModulatingSlave() const;
+ bool isPCM() const;
+ const ControlROMPCMStruct *getControlROMPCMStruct() const;
+ Synth *getSynth() const;
+
+ // Returns true only if data written to buffer
+ // This function (unlike the one below it) returns processed stereo samples
+ // made from combining this single partial with its pair, if it has one.
+ bool produceOutput(float *leftBuf, float *rightBuf, unsigned long length);
+
+ // This function writes mono sample output to the provided buffer, and returns the number of samples written
+ unsigned long generateSamples(float *partialBuf, unsigned long length);
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp
new file mode 100644
index 0000000000..42a3eaa179
--- /dev/null
+++ b/audio/softsynth/mt32/PartialManager.cpp
@@ -0,0 +1,250 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cstring>
+
+#include "mt32emu.h"
+#include "PartialManager.h"
+
+using namespace MT32Emu;
+
+PartialManager::PartialManager(Synth *useSynth, Part **useParts) {
+ synth = useSynth;
+ parts = useParts;
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ partialTable[i] = new Partial(synth, i);
+ }
+}
+
+PartialManager::~PartialManager(void) {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ delete partialTable[i];
+ }
+}
+
+void PartialManager::clearAlreadyOutputed() {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ partialTable[i]->alreadyOutputed = false;
+ }
+}
+
+bool PartialManager::shouldReverb(int i) {
+ return partialTable[i]->shouldReverb();
+}
+
+bool PartialManager::produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength) {
+ return partialTable[i]->produceOutput(leftBuf, rightBuf, bufferLength);
+}
+
+void PartialManager::deactivateAll() {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ partialTable[i]->deactivate();
+ }
+}
+
+unsigned int PartialManager::setReserve(Bit8u *rset) {
+ unsigned int pr = 0;
+ for (int x = 0; x <= 8; x++) {
+ numReservedPartialsForPart[x] = rset[x];
+ pr += rset[x];
+ }
+ return pr;
+}
+
+Partial *PartialManager::allocPartial(int partNum) {
+ Partial *outPartial = NULL;
+
+ // Get the first inactive partial
+ for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) {
+ if (!partialTable[partialNum]->isActive()) {
+ outPartial = partialTable[partialNum];
+ break;
+ }
+ }
+ if (outPartial != NULL) {
+ outPartial->activate(partNum);
+ }
+ return outPartial;
+}
+
+unsigned int PartialManager::getFreePartialCount(void) {
+ int count = 0;
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (!partialTable[i]->isActive()) {
+ count++;
+ }
+ }
+ return count;
+}
+
+// This function is solely used to gather data for debug output at the moment.
+void PartialManager::getPerPartPartialUsage(unsigned int perPartPartialUsage[9]) {
+ memset(perPartPartialUsage, 0, 9 * sizeof(unsigned int));
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->isActive()) {
+ perPartPartialUsage[partialTable[i]->getOwnerPart()]++;
+ }
+ }
+}
+
+// Finds the lowest-priority part that is exceeding its reserved partial allocation and has a poly
+// in POLY_Releasing, then kills its first releasing poly.
+// Parts with higher priority than minPart are not checked.
+// Assumes that getFreePartials() has been called to make numReservedPartialsForPart up-to-date.
+bool PartialManager::abortFirstReleasingPolyWhereReserveExceeded(int minPart) {
+ if (minPart == 8) {
+ // Rhythm is highest priority
+ minPart = -1;
+ }
+ for (int partNum = 7; partNum >= minPart; partNum--) {
+ int usePartNum = partNum == -1 ? 8 : partNum;
+ if (parts[usePartNum]->getActivePartialCount() > numReservedPartialsForPart[usePartNum]) {
+ // This part has exceeded its reserved partial count.
+ // If it has any releasing polys, kill its first one and we're done.
+ if (parts[usePartNum]->abortFirstPoly(POLY_Releasing)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Finds the lowest-priority part that is exceeding its reserved partial allocation and has a poly, then kills
+// its first poly in POLY_Held - or failing that, its first poly in any state.
+// Parts with higher priority than minPart are not checked.
+// Assumes that getFreePartials() has been called to make numReservedPartialsForPart up-to-date.
+bool PartialManager::abortFirstPolyPreferHeldWhereReserveExceeded(int minPart) {
+ if (minPart == 8) {
+ // Rhythm is highest priority
+ minPart = -1;
+ }
+ for (int partNum = 7; partNum >= minPart; partNum--) {
+ int usePartNum = partNum == -1 ? 8 : partNum;
+ if (parts[usePartNum]->getActivePartialCount() > numReservedPartialsForPart[usePartNum]) {
+ // This part has exceeded its reserved partial count.
+ // If it has any polys, kill its first (preferably held) one and we're done.
+ if (parts[usePartNum]->abortFirstPolyPreferHeld()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool PartialManager::freePartials(unsigned int needed, int partNum) {
+ // CONFIRMED: Barring bugs, this matches the real LAPC-I according to information from Mok.
+
+ // BUG: There's a bug in the LAPC-I implementation:
+ // When allocating for rhythm part, or when allocating for a part that is using fewer partials than it has reserved,
+ // held and playing polys on the rhythm part can potentially be aborted before releasing polys on the rhythm part.
+ // This bug isn't present on MT-32.
+ // I consider this to be a bug because I think that playing polys should always have priority over held polys,
+ // and held polys should always have priority over releasing polys.
+
+ // NOTE: This code generally aborts polys in parts (according to certain conditions) in the following order:
+ // 7, 6, 5, 4, 3, 2, 1, 0, 8 (rhythm)
+ // (from lowest priority, meaning most likely to have polys aborted, to highest priority, meaning least likely)
+
+ if (needed == 0) {
+ return true;
+ }
+
+ // Note that calling getFreePartialCount() also ensures that numReservedPartialsPerPart is up-to-date
+ if (getFreePartialCount() >= needed) {
+ return true;
+ }
+
+ // Note: These #ifdefs are temporary until we have proper "quirk" configuration.
+ // Also, the MT-32 version isn't properly confirmed yet.
+#ifdef MT32EMU_QUIRK_FREE_PARTIALS_MT32
+ // On MT-32, we bail out before even killing releasing partials if the allocating part has exceeded its reserve and is configured for priority-to-earlier-polys.
+ if (parts[partNum]->getActiveNonReleasingPartialCount() + needed > numReservedPartialsForPart[partNum] && (synth->getPart(partNum)->getPatchTemp()->patch.assignMode & 1)) {
+ return false;
+ }
+#endif
+
+ for (;;) {
+#ifdef MT32EMU_QUIRK_FREE_PARTIALS_MT32
+ // Abort releasing polys in parts that have exceeded their partial reservation (working backwards from part 7, with rhythm last)
+ if (!abortFirstReleasingPolyWhereReserveExceeded(-1)) {
+ break;
+ }
+#else
+ // Abort releasing polys in non-rhythm parts that have exceeded their partial reservation (working backwards from part 7)
+ if (!abortFirstReleasingPolyWhereReserveExceeded(0)) {
+ break;
+ }
+#endif
+ if (getFreePartialCount() >= needed) {
+ return true;
+ }
+ }
+
+ if (parts[partNum]->getActiveNonReleasingPartialCount() + needed > numReservedPartialsForPart[partNum]) {
+ // With the new partials we're freeing for, we would end up using more partials than we have reserved.
+ if (synth->getPart(partNum)->getPatchTemp()->patch.assignMode & 1) {
+ // Priority is given to earlier polys, so just give up
+ return false;
+ }
+ // Only abort held polys in the target part and parts that have a lower priority
+ // (higher part number = lower priority, except for rhythm, which has the highest priority).
+ for (;;) {
+ if (!abortFirstPolyPreferHeldWhereReserveExceeded(partNum)) {
+ break;
+ }
+ if (getFreePartialCount() >= needed) {
+ return true;
+ }
+ }
+ if (needed > numReservedPartialsForPart[partNum]) {
+ return false;
+ }
+ } else {
+ // At this point, we're certain that we've reserved enough partials to play our poly.
+ // Check all parts from lowest to highest priority to see whether they've exceeded their
+ // reserve, and abort their polys until until we have enough free partials or they're within
+ // their reserve allocation.
+ for (;;) {
+ if (!abortFirstPolyPreferHeldWhereReserveExceeded(-1)) {
+ break;
+ }
+ if (getFreePartialCount() >= needed) {
+ return true;
+ }
+ }
+ }
+
+ // Abort polys in the target part until there are enough free partials for the new one
+ for (;;) {
+ if (!parts[partNum]->abortFirstPolyPreferHeld()) {
+ break;
+ }
+ if (getFreePartialCount() >= needed) {
+ return true;
+ }
+ }
+
+ // Aww, not enough partials for you.
+ return false;
+}
+
+const Partial *PartialManager::getPartial(unsigned int partialNum) const {
+ if (partialNum > MT32EMU_MAX_PARTIALS - 1) {
+ return NULL;
+ }
+ return partialTable[partialNum];
+}
diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h
new file mode 100644
index 0000000000..bb78672457
--- /dev/null
+++ b/audio/softsynth/mt32/PartialManager.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_PARTIALMANAGER_H
+#define MT32EMU_PARTIALMANAGER_H
+
+namespace MT32Emu {
+
+class Synth;
+
+class PartialManager {
+private:
+ Synth *synth; // Only used for sending debug output
+ Part **parts;
+
+ Partial *partialTable[MT32EMU_MAX_PARTIALS];
+ Bit8u numReservedPartialsForPart[9];
+
+ bool abortFirstReleasingPolyWhereReserveExceeded(int minPart);
+ bool abortFirstPolyPreferHeldWhereReserveExceeded(int minPart);
+
+public:
+
+ PartialManager(Synth *synth, Part **parts);
+ ~PartialManager();
+ Partial *allocPartial(int partNum);
+ unsigned int getFreePartialCount(void);
+ void getPerPartPartialUsage(unsigned int perPartPartialUsage[9]);
+ bool freePartials(unsigned int needed, int partNum);
+ unsigned int setReserve(Bit8u *rset);
+ void deactivateAll();
+ bool produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength);
+ bool shouldReverb(int i);
+ void clearAlreadyOutputed();
+ const Partial *getPartial(unsigned int partialNum) const;
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp
new file mode 100644
index 0000000000..a2f00db73c
--- /dev/null
+++ b/audio/softsynth/mt32/Poly.cpp
@@ -0,0 +1,174 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+Poly::Poly(Part *usePart) {
+ part = usePart;
+ key = 255;
+ velocity = 255;
+ sustain = false;
+ activePartialCount = 0;
+ for (int i = 0; i < 4; i++) {
+ partials[i] = NULL;
+ }
+ state = POLY_Inactive;
+}
+
+void Poly::reset(unsigned int newKey, unsigned int newVelocity, bool newSustain, Partial **newPartials) {
+ if (isActive()) {
+ // FIXME: Throw out some big ugly debug output with a lot of exclamation marks - we should never get here
+ terminate();
+ }
+
+ key = newKey;
+ velocity = newVelocity;
+ sustain = newSustain;
+
+ activePartialCount = 0;
+ for (int i = 0; i < 4; i++) {
+ partials[i] = newPartials[i];
+ if (newPartials[i] != NULL) {
+ activePartialCount++;
+ state = POLY_Playing;
+ }
+ }
+}
+
+bool Poly::noteOff(bool pedalHeld) {
+ // Generally, non-sustaining instruments ignore note off. They die away eventually anyway.
+ // Key 0 (only used by special cases on rhythm part) reacts to note off even if non-sustaining or pedal held.
+ if (state == POLY_Inactive || state == POLY_Releasing) {
+ return false;
+ }
+ if (pedalHeld) {
+ state = POLY_Held;
+ } else {
+ startDecay();
+ }
+ return true;
+}
+
+bool Poly::stopPedalHold() {
+ if (state != POLY_Held) {
+ return false;
+ }
+ return startDecay();
+}
+
+bool Poly::startDecay() {
+ if (state == POLY_Inactive || state == POLY_Releasing) {
+ return false;
+ }
+ state = POLY_Releasing;
+
+ for (int t = 0; t < 4; t++) {
+ Partial *partial = partials[t];
+ if (partial != NULL) {
+ partial->startDecayAll();
+ }
+ }
+ return true;
+}
+
+bool Poly::startAbort() {
+ if (state == POLY_Inactive) {
+ return false;
+ }
+ for (int t = 0; t < 4; t++) {
+ Partial *partial = partials[t];
+ if (partial != NULL) {
+ partial->startAbort();
+ }
+ }
+ return true;
+}
+
+void Poly::terminate() {
+ if (state == POLY_Inactive) {
+ return;
+ }
+ for (int t = 0; t < 4; t++) {
+ Partial *partial = partials[t];
+ if (partial != NULL) {
+ partial->deactivate();
+ }
+ }
+ if (state != POLY_Inactive) {
+ // FIXME: Throw out lots of debug output - this should never happen
+ // (Deactivating the partials above should've made them each call partialDeactivated(), ultimately changing the state to POLY_Inactive)
+ state = POLY_Inactive;
+ }
+}
+
+void Poly::backupCacheToPartials(PatchCache cache[4]) {
+ for (int partialNum = 0; partialNum < 4; partialNum++) {
+ Partial *partial = partials[partialNum];
+ if (partial != NULL && partial->patchCache == &cache[partialNum]) {
+ partial->cachebackup = cache[partialNum];
+ partial->patchCache = &partial->cachebackup;
+ }
+ }
+}
+
+/**
+ * Returns the internal key identifier.
+ * For non-rhythm, this is within the range 12 to 108.
+ * For rhythm on MT-32, this is 0 or 1 (special cases) or within the range 24 to 87.
+ * For rhythm on devices with extended PCM sounds (e.g. CM-32L), this is 0, 1 or 24 to 108
+ */
+unsigned int Poly::getKey() const {
+ return key;
+}
+
+unsigned int Poly::getVelocity() const {
+ return velocity;
+}
+
+bool Poly::canSustain() const {
+ return sustain;
+}
+
+PolyState Poly::getState() const {
+ return state;
+}
+
+unsigned int Poly::getActivePartialCount() const {
+ return activePartialCount;
+}
+
+bool Poly::isActive() const {
+ return state != POLY_Inactive;
+}
+
+// This is called by Partial to inform the poly that the Partial has deactivated
+void Poly::partialDeactivated(Partial *partial) {
+ for (int i = 0; i < 4; i++) {
+ if (partials[i] == partial) {
+ partials[i] = NULL;
+ activePartialCount--;
+ }
+ }
+ if (activePartialCount == 0) {
+ state = POLY_Inactive;
+ }
+ part->partialDeactivated(this);
+}
+
+}
diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h
new file mode 100644
index 0000000000..cd15a776f5
--- /dev/null
+++ b/audio/softsynth/mt32/Poly.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_POLY_H
+#define MT32EMU_POLY_H
+
+namespace MT32Emu {
+
+class Part;
+
+enum PolyState {
+ POLY_Playing,
+ POLY_Held, // This marks keys that have been released on the keyboard, but are being held by the pedal
+ POLY_Releasing,
+ POLY_Inactive
+};
+
+class Poly {
+private:
+ Part *part;
+ unsigned int key;
+ unsigned int velocity;
+ unsigned int activePartialCount;
+ bool sustain;
+
+ PolyState state;
+
+ Partial *partials[4];
+
+public:
+ Poly(Part *part);
+ void reset(unsigned int key, unsigned int velocity, bool sustain, Partial **partials);
+ bool noteOff(bool pedalHeld);
+ bool stopPedalHold();
+ bool startDecay();
+ bool startAbort();
+ void terminate();
+
+ void backupCacheToPartials(PatchCache cache[4]);
+
+ unsigned int getKey() const;
+ unsigned int getVelocity() const;
+ bool canSustain() const;
+ PolyState getState() const;
+ unsigned int getActivePartialCount() const;
+ bool isActive() const;
+
+ void partialDeactivated(Partial *partial);
+};
+
+}
+
+#endif /* POLY_H_ */
diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h
new file mode 100644
index 0000000000..e4d98ad1fc
--- /dev/null
+++ b/audio/softsynth/mt32/Structures.h
@@ -0,0 +1,217 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_STRUCTURES_H
+#define MT32EMU_STRUCTURES_H
+
+namespace MT32Emu {
+
+// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it
+// Roland provides documentation using the sysex-padded addresses, so we tend to use that in code and output
+#define MT32EMU_MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f))
+#define MT32EMU_SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f))
+
+#ifdef _MSC_VER
+#define MT32EMU_ALIGN_PACKED __declspec(align(1))
+#else
+#define MT32EMU_ALIGN_PACKED __attribute__((packed))
+#endif
+
+typedef unsigned int Bit32u;
+typedef signed int Bit32s;
+typedef unsigned short int Bit16u;
+typedef signed short int Bit16s;
+typedef unsigned char Bit8u;
+typedef signed char Bit8s;
+
+// The following structures represent the MT-32's memory
+// Since sysex allows this memory to be written to in blocks of bytes,
+// we keep this packed so that we can copy data into the various
+// banks directly
+#if defined(_MSC_VER) || defined (__MINGW32__)
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+struct TimbreParam {
+ struct CommonParam {
+ char name[10];
+ Bit8u partialStructure12; // 1 & 2 0-12 (1-13)
+ Bit8u partialStructure34; // 3 & 4 0-12 (1-13)
+ Bit8u partialMute; // 0-15 (0000-1111)
+ Bit8u noSustain; // ENV MODE 0-1 (Normal, No sustain)
+ } MT32EMU_ALIGN_PACKED common;
+
+ struct PartialParam {
+ struct WGParam {
+ Bit8u pitchCoarse; // 0-96 (C1,C#1-C9)
+ Bit8u pitchFine; // 0-100 (-50 to +50 (cents - confirmed by Mok))
+ Bit8u pitchKeyfollow; // 0-16 (-1, -1/2, -1/4, 0, 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, 1, 5/4, 3/2, 2, s1, s2)
+ Bit8u pitchBenderEnabled; // 0-1 (OFF, ON)
+ Bit8u waveform; // MT-32: 0-1 (SQU/SAW); LAPC-I: WG WAVEFORM/PCM BANK 0 - 3 (SQU/1, SAW/1, SQU/2, SAW/2)
+ Bit8u pcmWave; // 0-127 (1-128)
+ Bit8u pulseWidth; // 0-100
+ Bit8u pulseWidthVeloSensitivity; // 0-14 (-7 - +7)
+ } MT32EMU_ALIGN_PACKED wg;
+
+ struct PitchEnvParam {
+ Bit8u depth; // 0-10
+ Bit8u veloSensitivity; // 0-100
+ Bit8u timeKeyfollow; // 0-4
+ Bit8u time[4]; // 0-100
+ Bit8u level[5]; // 0-100 (-50 - +50) // [3]: SUSTAIN LEVEL, [4]: END LEVEL
+ } MT32EMU_ALIGN_PACKED pitchEnv;
+
+ struct PitchLFOParam {
+ Bit8u rate; // 0-100
+ Bit8u depth; // 0-100
+ Bit8u modSensitivity; // 0-100
+ } MT32EMU_ALIGN_PACKED pitchLFO;
+
+ struct TVFParam {
+ Bit8u cutoff; // 0-100
+ Bit8u resonance; // 0-30
+ Bit8u keyfollow; // -1, -1/2, -1/4, 0, 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, 1, 5/4, 3/2, 2
+ Bit8u biasPoint; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biasLevel; // 0-14 (-7 - +7)
+ Bit8u envDepth; // 0-100
+ Bit8u envVeloSensitivity; // 0-100
+ Bit8u envDepthKeyfollow; // DEPTH KEY FOLL0W 0-4
+ Bit8u envTimeKeyfollow; // TIME KEY FOLLOW 0-4
+ Bit8u envTime[5]; // 0-100
+ Bit8u envLevel[4]; // 0-100 // [3]: SUSTAIN LEVEL
+ } MT32EMU_ALIGN_PACKED tvf;
+
+ struct TVAParam {
+ Bit8u level; // 0-100
+ Bit8u veloSensitivity; // 0-100
+ Bit8u biasPoint1; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biasLevel1; // 0-12 (-12 - 0)
+ Bit8u biasPoint2; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biasLevel2; // 0-12 (-12 - 0)
+ Bit8u envTimeKeyfollow; // TIME KEY FOLLOW 0-4
+ Bit8u envTimeVeloSensitivity; // VELOS KEY FOLL0W 0-4
+ Bit8u envTime[5]; // 0-100
+ Bit8u envLevel[4]; // 0-100 // [3]: SUSTAIN LEVEL
+ } MT32EMU_ALIGN_PACKED tva;
+ } MT32EMU_ALIGN_PACKED partial[4];
+} MT32EMU_ALIGN_PACKED;
+
+struct PatchParam {
+ Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
+ Bit8u timbreNum; // TIMBRE NUMBER 0-63
+ Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones)
+ Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents)
+ Bit8u benderRange; // BENDER RANGE 0-24
+ Bit8u assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
+ Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ Bit8u dummy; // (DUMMY)
+} MT32EMU_ALIGN_PACKED;
+
+const unsigned int SYSTEM_MASTER_TUNE_OFF = 0;
+const unsigned int SYSTEM_REVERB_MODE_OFF = 1;
+const unsigned int SYSTEM_REVERB_TIME_OFF = 2;
+const unsigned int SYSTEM_REVERB_LEVEL_OFF = 3;
+const unsigned int SYSTEM_RESERVE_SETTINGS_START_OFF = 4;
+const unsigned int SYSTEM_RESERVE_SETTINGS_END_OFF = 12;
+const unsigned int SYSTEM_CHAN_ASSIGN_START_OFF = 13;
+const unsigned int SYSTEM_CHAN_ASSIGN_END_OFF = 21;
+const unsigned int SYSTEM_MASTER_VOL_OFF = 22;
+
+struct MemParams {
+ // NOTE: The MT-32 documentation only specifies PatchTemp areas for parts 1-8.
+ // The LAPC-I documentation specified an additional area for rhythm at the end,
+ // where all parameters but fine tune, assign mode and output level are ignored
+ struct PatchTemp {
+ PatchParam patch;
+ Bit8u outputLevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u dummyv[6];
+ } MT32EMU_ALIGN_PACKED patchTemp[9];
+
+ struct RhythmTemp {
+ Bit8u timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF); LAPC-I: 0-127 (M01-M64,R01-R63)
+ Bit8u outputLevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ } MT32EMU_ALIGN_PACKED rhythmTemp[85];
+
+ TimbreParam timbreTemp[8];
+
+ PatchParam patches[128];
+
+ // NOTE: There are only 30 timbres in the "rhythm" bank for MT-32; the additional 34 are for LAPC-I and above
+ struct PaddedTimbre {
+ TimbreParam timbre;
+ Bit8u padding[10];
+ } MT32EMU_ALIGN_PACKED timbres[64 + 64 + 64 + 64]; // Group A, Group B, Memory, Rhythm
+
+ struct System {
+ Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
+ Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
+ Bit8u reverbTime; // REVERB TIME 0-7 (1-8)
+ Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8)
+ Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
+ Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
+ Bit8u masterVol; // MASTER VOLUME 0-100
+ } MT32EMU_ALIGN_PACKED system;
+};
+
+#if defined(_MSC_VER) || defined (__MINGW32__)
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+struct ControlROMPCMStruct;
+
+struct PCMWaveEntry {
+ Bit32u addr;
+ Bit32u len;
+ bool loop;
+ ControlROMPCMStruct *controlROMPCMStruct;
+};
+
+// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings
+struct PatchCache {
+ bool playPartial;
+ bool PCMPartial;
+ int pcm;
+ char waveform;
+
+ Bit32u structureMix;
+ int structurePosition;
+ int structurePair;
+
+ // The following fields are actually common to all partials in the timbre
+ bool dirty;
+ Bit32u partialCount;
+ bool sustain;
+ bool reverb;
+
+ TimbreParam::PartialParam srcPartial;
+
+ // The following directly points into live sysex-addressable memory
+ const TimbreParam::PartialParam *partialParam;
+};
+
+class Partial; // Forward reference for class defined in partial.h
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
new file mode 100644
index 0000000000..0861053b5c
--- /dev/null
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -0,0 +1,1620 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cerrno>
+//#include <cmath>
+//#include <cstdlib>
+//#include <cstring>
+
+#define FORBIDDEN_SYMBOL_EXCEPTION_printf
+#define FORBIDDEN_SYMBOL_EXCEPTION_vprintf
+
+#include "mt32emu.h"
+#include "mmath.h"
+#include "PartialManager.h"
+
+#if MT32EMU_USE_AREVERBMODEL == 1
+#include "AReverbModel.h"
+#else
+#include "FreeverbModel.h"
+#endif
+#include "DelayReverb.h"
+
+namespace MT32Emu {
+
+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},
+ {0x4014, 22, "\000 ver1.05 06 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
+ {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C},
+ {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4}, // MT-32 revision 1
+ {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228}, // MT-32 Blue Ridge mod
+ {0x2205, 22, "\000CM32/LAPC1.00 890404", 0x8100, 256, 0x8000, 0x8000, false, 0x8080, 0x8000, false, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5},
+ {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF} // CM-32L
+ // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
+};
+
+static inline Bit16s *streamOffset(Bit16s *stream, Bit32u pos) {
+ return stream == NULL ? NULL : stream + pos;
+}
+
+static inline void clearIfNonNull(Bit16s *stream, Bit32u len) {
+ if (stream != NULL) {
+ memset(stream, 0, len * sizeof(Bit16s));
+ }
+}
+
+static inline void mix(float *target, const float *stream, Bit32u len) {
+ while (len--) {
+ *target += *stream;
+ stream++;
+ target++;
+ }
+}
+
+static inline void clearFloats(float *leftBuf, float *rightBuf, Bit32u len) {
+ // FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win)
+ while (len--) {
+ *leftBuf++ = 0.0f;
+ *rightBuf++ = 0.0f;
+ }
+}
+
+static inline Bit16s clipBit16s(Bit32s a) {
+ // Clamp values above 32767 to 32767, and values below -32768 to -32768
+ if ((a + 32768) & ~65535) {
+ return (a >> 31) ^ 32767;
+ }
+ return a;
+}
+
+static void floatToBit16s_nice(Bit16s *target, const float *source, Bit32u len, float outputGain) {
+ float gain = outputGain * 16384.0f;
+ while (len--) {
+ // Since we're not shooting for accuracy here, don't worry about the rounding mode.
+ *target = clipBit16s((Bit32s)(*source * gain));
+ source++;
+ target++;
+ }
+}
+
+static void floatToBit16s_pure(Bit16s *target, const float *source, Bit32u len, float /*outputGain*/) {
+ while (len--) {
+ *target = clipBit16s((Bit32s)floor(*source * 8192.0f));
+ source++;
+ target++;
+ }
+}
+
+static void floatToBit16s_reverb(Bit16s *target, const float *source, Bit32u len, float outputGain) {
+ float gain = outputGain * 8192.0f;
+ while (len--) {
+ *target = clipBit16s((Bit32s)floor(*source * gain));
+ source++;
+ target++;
+ }
+}
+
+static void floatToBit16s_generation1(Bit16s *target, const float *source, Bit32u len, float outputGain) {
+ float gain = outputGain * 8192.0f;
+ while (len--) {
+ *target = clipBit16s((Bit32s)floor(*source * gain));
+ *target = (*target & 0x8000) | ((*target << 1) & 0x7FFE);
+ source++;
+ target++;
+ }
+}
+
+static void floatToBit16s_generation2(Bit16s *target, const float *source, Bit32u len, float outputGain) {
+ float gain = outputGain * 8192.0f;
+ while (len--) {
+ *target = clipBit16s((Bit32s)floor(*source * gain));
+ *target = (*target & 0x8000) | ((*target << 1) & 0x7FFE) | ((*target >> 14) & 0x0001);
+ source++;
+ target++;
+ }
+}
+
+Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
+ for (unsigned int i = 0; i < len; i++) {
+ checksum = checksum + data[i];
+ }
+ checksum = checksum & 0x7f;
+ if (checksum) {
+ checksum = 0x80 - checksum;
+ }
+ return checksum;
+}
+
+Synth::Synth() {
+ isOpen = false;
+ reverbEnabled = true;
+ reverbOverridden = false;
+
+#if MT32EMU_USE_AREVERBMODEL == 1
+ reverbModels[0] = new AReverbModel(&AReverbModel::REVERB_MODE_0_SETTINGS);
+ reverbModels[1] = new AReverbModel(&AReverbModel::REVERB_MODE_1_SETTINGS);
+ reverbModels[2] = new AReverbModel(&AReverbModel::REVERB_MODE_2_SETTINGS);
+#else
+ reverbModels[0] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f);
+ reverbModels[1] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f);
+ reverbModels[2] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f);
+#endif
+
+ reverbModels[3] = new DelayReverb();
+ reverbModel = NULL;
+ setDACInputMode(DACInputMode_NICE);
+ setOutputGain(1.0f);
+ setReverbOutputGain(0.68f);
+ partialManager = NULL;
+ memset(parts, 0, sizeof(parts));
+ renderedSampleCount = 0;
+}
+
+Synth::~Synth() {
+ close(); // Make sure we're closed and everything is freed
+ for (int i = 0; i < 4; i++) {
+ delete reverbModels[i];
+ }
+}
+
+int Synth::report(ReportType type, const void *data) {
+ if (myProp.report != NULL) {
+ return myProp.report(myProp.userData, type, data);
+ }
+ return 0;
+}
+
+unsigned int Synth::getSampleRate() const {
+ return myProp.sampleRate;
+}
+
+void Synth::printDebug(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ if (myProp.printDebug != NULL) {
+ myProp.printDebug(myProp.userData, fmt, ap);
+ } else {
+#if MT32EMU_DEBUG_SAMPLESTAMPS > 0
+ printf("[%u] ", renderedSampleCount);
+#endif
+ vprintf(fmt, ap);
+ printf("\n");
+ }
+ va_end(ap);
+}
+
+void Synth::setReverbEnabled(bool newReverbEnabled) {
+ reverbEnabled = newReverbEnabled;
+}
+
+bool Synth::isReverbEnabled() const {
+ return reverbEnabled;
+}
+
+void Synth::setReverbOverridden(bool newReverbOverridden) {
+ reverbOverridden = newReverbOverridden;
+}
+
+bool Synth::isReverbOverridden() const {
+ return reverbOverridden;
+}
+
+void Synth::setDACInputMode(DACInputMode mode) {
+ switch(mode) {
+ case DACInputMode_GENERATION1:
+ la32FloatToBit16sFunc = floatToBit16s_generation1;
+ reverbFloatToBit16sFunc = floatToBit16s_reverb;
+ break;
+ case DACInputMode_GENERATION2:
+ la32FloatToBit16sFunc = floatToBit16s_generation2;
+ reverbFloatToBit16sFunc = floatToBit16s_reverb;
+ break;
+ case DACInputMode_PURE:
+ la32FloatToBit16sFunc = floatToBit16s_pure;
+ reverbFloatToBit16sFunc = floatToBit16s_pure;
+ break;
+ case DACInputMode_NICE:
+ default:
+ la32FloatToBit16sFunc = floatToBit16s_nice;
+ reverbFloatToBit16sFunc = floatToBit16s_reverb;
+ break;
+ }
+}
+
+void Synth::setOutputGain(float newOutputGain) {
+ outputGain = newOutputGain;
+}
+
+void Synth::setReverbOutputGain(float newReverbOutputGain) {
+ reverbOutputGain = newReverbOutputGain;
+}
+
+Common::File *Synth::openFile(const char *filename) {
+ if (myProp.openFile != NULL) {
+ return myProp.openFile(myProp.userData, filename);
+ }
+ char pathBuf[2048];
+ if (myProp.baseDir != NULL) {
+ strcpy(&pathBuf[0], myProp.baseDir);
+ strcat(&pathBuf[0], filename);
+ filename = pathBuf;
+ }
+ Common::File *file = new Common::File();
+ if (!file->open(filename)) {
+ delete file;
+ return NULL;
+ }
+ return file;
+}
+
+void Synth::closeFile(Common::File *file) {
+ if (myProp.closeFile != NULL) {
+ myProp.closeFile(myProp.userData, file);
+ } else {
+ file->close();
+ delete file;
+ }
+}
+
+LoadResult Synth::loadControlROM(const char *filename) {
+ Common::File *file = openFile(filename); // ROM File
+ if (file == NULL) {
+ return LoadResult_NotFound;
+ }
+ size_t fileSize = file->size();
+ if (fileSize != CONTROL_ROM_SIZE) {
+ printDebug("Control ROM file %s size mismatch: %i", filename, fileSize);
+ }
+ file->read(controlROMData, CONTROL_ROM_SIZE);
+ if (file->err()) {
+ closeFile(file);
+ return LoadResult_Unreadable;
+ }
+ closeFile(file);
+
+ // Control ROM successfully loaded, now check whether it's a known type
+ controlROMMap = NULL;
+ for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) {
+ if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) {
+ controlROMMap = &ControlROMMaps[i];
+ return LoadResult_OK;
+ }
+ }
+ printDebug("%s does not match a known control ROM type", filename);
+ return LoadResult_Invalid;
+}
+
+LoadResult Synth::loadPCMROM(const char *filename) {
+ Common::File *file = openFile(filename); // ROM File
+ if (file == NULL) {
+ return LoadResult_NotFound;
+ }
+ size_t fileSize = file->size();
+ if (fileSize < (size_t)(2 * pcmROMSize)) {
+ printDebug("PCM ROM file is too short (expected %d, got %d)", 2 * pcmROMSize, fileSize);
+ closeFile(file);
+ return LoadResult_Invalid;
+ }
+ if (file->err()) {
+ closeFile(file);
+ return LoadResult_Unreadable;
+ }
+ LoadResult rc = LoadResult_OK;
+ for (int i = 0; i < pcmROMSize; i++) {
+ Bit8u s = file->readByte();
+ Bit8u c = file->readByte();
+
+ int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
+
+ signed short log = 0;
+ for (int u = 0; u < 15; u++) {
+ int bit;
+ if (order[u] < 8) {
+ bit = (s >> (7 - order[u])) & 0x1;
+ } else {
+ bit = (c >> (7 - (order[u] - 8))) & 0x1;
+ }
+ log = log | (short)(bit << (15 - u));
+ }
+ bool negative = log < 0;
+ log &= 0x7FFF;
+
+ // CONFIRMED from sample analysis to be 99.99%+ accurate with current TVA multiplier
+ float lin = EXP2F((32787 - log) / -2048.0f);
+
+ if (negative) {
+ lin = -lin;
+ }
+
+ pcmROMData[i] = lin;
+ }
+ closeFile(file);
+ return rc;
+}
+
+bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
+ ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
+ for (int i = 0; i < count; i++) {
+ int rAddr = tps[i].pos * 0x800;
+ int rLenExp = (tps[i].len & 0x70) >> 4;
+ int rLen = 0x800 << rLenExp;
+ if (rAddr + rLen > pcmROMSize) {
+ printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen);
+ return false;
+ }
+ pcmWaves[i].addr = rAddr;
+ pcmWaves[i].len = rLen;
+ pcmWaves[i].loop = (tps[i].len & 0x80) != 0;
+ pcmWaves[i].controlROMPCMStruct = &tps[i];
+ //int pitch = (tps[i].pitchMSB << 8) | tps[i].pitchLSB;
+ //bool unaffectedByMasterTune = (tps[i].len & 0x01) == 0;
+ //printDebug("PCM %d: pos=%d, len=%d, pitch=%d, loop=%s, unaffectedByMasterTune=%s", i, rAddr, rLen, pitch, pcmWaves[i].loop ? "YES" : "NO", unaffectedByMasterTune ? "YES" : "NO");
+ }
+ return false;
+}
+
+bool Synth::initCompressedTimbre(int timbreNum, const Bit8u *src, unsigned int srcLen) {
+ // "Compressed" here means that muted partials aren't present in ROM (except in the case of partial 0 being muted).
+ // Instead the data from the previous unmuted partial is used.
+ if (srcLen < sizeof(TimbreParam::CommonParam)) {
+ return false;
+ }
+ TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
+ timbresMemoryRegion->write(timbreNum, 0, src, sizeof(TimbreParam::CommonParam), true);
+ unsigned int srcPos = sizeof(TimbreParam::CommonParam);
+ unsigned int memPos = sizeof(TimbreParam::CommonParam);
+ for (int t = 0; t < 4; t++) {
+ if (t != 0 && ((timbre->common.partialMute >> t) & 0x1) == 0x00) {
+ // This partial is muted - we'll copy the previously copied partial, then
+ srcPos -= sizeof(TimbreParam::PartialParam);
+ } else if (srcPos + sizeof(TimbreParam::PartialParam) >= srcLen) {
+ return false;
+ }
+ timbresMemoryRegion->write(timbreNum, memPos, src + srcPos, sizeof(TimbreParam::PartialParam));
+ srcPos += sizeof(TimbreParam::PartialParam);
+ memPos += sizeof(TimbreParam::PartialParam);
+ }
+ return true;
+}
+
+bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTimbre, bool compressed) {
+ const Bit8u *timbreMap = &controlROMData[mapAddress];
+ for (Bit16u i = 0; i < count * 2; i += 2) {
+ Bit16u address = (timbreMap[i + 1] << 8) | timbreMap[i];
+ if (!compressed && (address + offset + sizeof(TimbreParam) > CONTROL_ROM_SIZE)) {
+ printDebug("Control ROM error: Timbre map entry 0x%04x for timbre %d points to invalid timbre address 0x%04x", i, startTimbre, address);
+ return false;
+ }
+ address += offset;
+ if (compressed) {
+ if (!initCompressedTimbre(startTimbre, &controlROMData[address], CONTROL_ROM_SIZE - address)) {
+ printDebug("Control ROM error: Timbre map entry 0x%04x for timbre %d points to invalid timbre at 0x%04x", i, startTimbre, address);
+ return false;
+ }
+ } else {
+ timbresMemoryRegion->write(startTimbre, 0, &controlROMData[address], sizeof(TimbreParam), true);
+ }
+ startTimbre++;
+ }
+ return true;
+}
+
+bool Synth::open(SynthProperties &useProp) {
+ if (isOpen) {
+ return false;
+ }
+ prerenderReadIx = prerenderWriteIx = 0;
+ myProp = useProp;
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising Constant Tables");
+#endif
+ tables.init();
+#if !MT32EMU_REDUCE_REVERB_MEMORY
+ for (int i = 0; i < 4; i++) {
+ reverbModels[i]->open(useProp.sampleRate);
+ }
+#endif
+ if (useProp.baseDir != NULL) {
+ char *baseDirCopy = new char[strlen(useProp.baseDir) + 1];
+ strcpy(baseDirCopy, useProp.baseDir);
+ myProp.baseDir = baseDirCopy;
+ }
+
+ // This is to help detect bugs
+ memset(&mt32ram, '?', sizeof(mt32ram));
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Loading Control ROM");
+#endif
+ if (loadControlROM("CM32L_CONTROL.ROM") != LoadResult_OK) {
+ if (loadControlROM("MT32_CONTROL.ROM") != LoadResult_OK) {
+ printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM");
+ //report(ReportType_errorControlROM, &errno);
+ return false;
+ }
+ }
+
+ initMemoryRegions();
+
+ // 512KB PCM ROM for MT-32, etc.
+ // 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500
+ // Note that the size below is given in samples (16-bit), not bytes
+ pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024;
+ pcmROMData = new float[pcmROMSize];
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Loading PCM ROM");
+#endif
+ if (loadPCMROM("CM32L_PCM.ROM") != LoadResult_OK) {
+ if (loadPCMROM("MT32_PCM.ROM") != LoadResult_OK) {
+ printDebug("Init Error - Missing MT32_PCM.ROM");
+ //report(ReportType_errorPCMROM, &errno);
+ return false;
+ }
+ }
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising Timbre Bank A");
+#endif
+ if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0x40, 0, controlROMMap->timbreACompressed)) {
+ return false;
+ }
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising Timbre Bank B");
+#endif
+ if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 0x40, 64, controlROMMap->timbreBCompressed)) {
+ return false;
+ }
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising Timbre Bank R");
+#endif
+ if (!initTimbres(controlROMMap->timbreRMap, 0, controlROMMap->timbreRCount, 192, true)) {
+ return false;
+ }
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising Timbre Bank M");
+#endif
+ // CM-64 seems to initialise all bytes in this bank to 0.
+ memset(&mt32ram.timbres[128], 0, sizeof(mt32ram.timbres[128]) * 64);
+
+ partialManager = new PartialManager(this, parts);
+
+ pcmWaves = new PCMWaveEntry[controlROMMap->pcmCount];
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising PCM List");
+#endif
+ initPCMList(controlROMMap->pcmTable, controlROMMap->pcmCount);
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising Rhythm Temp");
+#endif
+ memcpy(mt32ram.rhythmTemp, &controlROMData[controlROMMap->rhythmSettings], controlROMMap->rhythmSettingsCount * 4);
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising Patches");
+#endif
+ for (Bit8u i = 0; i < 128; i++) {
+ PatchParam *patch = &mt32ram.patches[i];
+ patch->timbreGroup = i / 64;
+ patch->timbreNum = i % 64;
+ patch->keyShift = 24;
+ patch->fineTune = 50;
+ patch->benderRange = 12;
+ patch->assignMode = 0;
+ patch->reverbSwitch = 1;
+ patch->dummy = 0;
+ }
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("Initialising System");
+#endif
+ // The MT-32 manual claims that "Standard pitch" is 442Hz.
+ mt32ram.system.masterTune = 0x4A; // Confirmed on CM-64
+ mt32ram.system.reverbMode = 0; // Confirmed
+ mt32ram.system.reverbTime = 5; // Confirmed
+ mt32ram.system.reverbLevel = 3; // Confirmed
+ memcpy(mt32ram.system.reserveSettings, &controlROMData[controlROMMap->reserveSettings], 9); // Confirmed
+ for (Bit8u i = 0; i < 9; i++) {
+ // This is the default: {1, 2, 3, 4, 5, 6, 7, 8, 9}
+ // An alternative configuration can be selected by holding "Master Volume"
+ // and pressing "PART button 1" on the real MT-32's frontpanel.
+ // The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9}
+ mt32ram.system.chanAssign[i] = i + 1;
+ }
+ mt32ram.system.masterVol = 100; // Confirmed
+ refreshSystem();
+
+ for (int i = 0; i < 9; i++) {
+ MemParams::PatchTemp *patchTemp = &mt32ram.patchTemp[i];
+
+ // Note that except for the rhythm part, these patch fields will be set in setProgram() below anyway.
+ patchTemp->patch.timbreGroup = 0;
+ patchTemp->patch.timbreNum = 0;
+ patchTemp->patch.keyShift = 24;
+ patchTemp->patch.fineTune = 50;
+ patchTemp->patch.benderRange = 12;
+ patchTemp->patch.assignMode = 0;
+ patchTemp->patch.reverbSwitch = 1;
+ patchTemp->patch.dummy = 0;
+
+ patchTemp->outputLevel = 80;
+ patchTemp->panpot = controlROMData[controlROMMap->panSettings + i];
+ memset(patchTemp->dummyv, 0, sizeof(patchTemp->dummyv));
+ patchTemp->dummyv[1] = 127;
+
+ if (i < 8) {
+ parts[i] = new Part(this, i);
+ parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
+ } else {
+ parts[i] = new RhythmPart(this, i);
+ }
+ }
+
+ // For resetting mt32 mid-execution
+ mt32default = mt32ram;
+
+ isOpen = true;
+ isEnabled = false;
+
+#if MT32EMU_MONITOR_INIT
+ printDebug("*** Initialisation complete ***");
+#endif
+ return true;
+}
+
+void Synth::close() {
+ if (!isOpen) {
+ return;
+ }
+
+ delete partialManager;
+ partialManager = NULL;
+
+ for (int i = 0; i < 9; i++) {
+ delete parts[i];
+ parts[i] = NULL;
+ }
+
+ delete[] myProp.baseDir;
+ myProp.baseDir = NULL;
+
+ delete[] pcmWaves;
+ delete[] pcmROMData;
+
+ deleteMemoryRegions();
+
+ for (int i = 0; i < 4; i++) {
+ reverbModels[i]->close();
+ }
+ reverbModel = NULL;
+ isOpen = false;
+}
+
+void Synth::playMsg(Bit32u msg) {
+ // FIXME: Implement active sensing
+ unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
+ unsigned char chan = (unsigned char)(msg & 0x00000F);
+ unsigned char note = (unsigned char)((msg & 0x00FF00) >> 8);
+ unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16);
+ isEnabled = true;
+
+ //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
+
+ char part = chantable[chan];
+ if (part < 0 || part > 8) {
+#if MT32EMU_MONITOR_MIDI > 0
+ printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity);
+#endif
+ return;
+ }
+ playMsgOnPart(part, code, note, velocity);
+}
+
+void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
+ Bit32u bend;
+
+ //printDebug("Synth::playMsgOnPart(%02x, %02x, %02x, %02x)", part, code, note, velocity);
+ switch (code) {
+ case 0x8:
+ //printDebug("Note OFF - Part %d", part);
+ // The MT-32 ignores velocity for note off
+ parts[part]->noteOff(note);
+ break;
+ case 0x9:
+ //printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity);
+ if (velocity == 0) {
+ // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40
+ parts[part]->noteOff(note);
+ } else {
+ parts[part]->noteOn(note, velocity);
+ }
+ break;
+ case 0xB: // Control change
+ switch (note) {
+ case 0x01: // Modulation
+ //printDebug("Modulation: %d", velocity);
+ parts[part]->setModulation(velocity);
+ break;
+ case 0x06:
+ parts[part]->setDataEntryMSB(velocity);
+ break;
+ case 0x07: // Set volume
+ //printDebug("Volume set: %d", velocity);
+ parts[part]->setVolume(velocity);
+ break;
+ case 0x0A: // Pan
+ //printDebug("Pan set: %d", velocity);
+ parts[part]->setPan(velocity);
+ break;
+ case 0x0B:
+ //printDebug("Expression set: %d", velocity);
+ parts[part]->setExpression(velocity);
+ break;
+ case 0x40: // Hold (sustain) pedal
+ //printDebug("Hold pedal set: %d", velocity);
+ parts[part]->setHoldPedal(velocity >= 64);
+ break;
+
+ case 0x62:
+ case 0x63:
+ parts[part]->setNRPN();
+ break;
+ case 0x64:
+ parts[part]->setRPNLSB(velocity);
+ break;
+ case 0x65:
+ parts[part]->setRPNMSB(velocity);
+ break;
+
+ case 0x79: // Reset all controllers
+ //printDebug("Reset all controllers");
+ parts[part]->resetAllControllers();
+ break;
+
+ case 0x7B: // All notes off
+ //printDebug("All notes off");
+ parts[part]->allNotesOff();
+ break;
+
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ // CONFIRMED:Mok: A real LAPC-I responds to these controllers as follows:
+ parts[part]->setHoldPedal(false);
+ parts[part]->allNotesOff();
+ break;
+
+ default:
+#if MT32EMU_MONITOR_MIDI > 0
+ printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity);
+#endif
+ break;
+ }
+
+ break;
+ case 0xC: // Program change
+ //printDebug("Program change %01x", note);
+ parts[part]->setProgram(note);
+ break;
+ case 0xE: // Pitch bender
+ bend = (velocity << 7) | (note);
+ //printDebug("Pitch bender %02x", bend);
+ parts[part]->setBend(bend);
+ break;
+ default:
+#if MT32EMU_MONITOR_MIDI > 0
+ printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
+#endif
+ break;
+ }
+
+ //midiOutShortMsg(m_out, msg);
+}
+
+void Synth::playSysex(const Bit8u *sysex, Bit32u len) {
+ if (len < 2) {
+ printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
+ }
+ if (sysex[0] != 0xF0) {
+ printDebug("playSysex: Message lacks start-of-sysex (0xF0)");
+ return;
+ }
+ // Due to some programs (e.g. Java) sending buffers with junk at the end, we have to go through and find the end marker rather than relying on len.
+ Bit32u endPos;
+ for (endPos = 1; endPos < len; endPos++) {
+ if (sysex[endPos] == 0xF7) {
+ break;
+ }
+ }
+ if (endPos == len) {
+ printDebug("playSysex: Message lacks end-of-sysex (0xf7)");
+ return;
+ }
+ playSysexWithoutFraming(sysex + 1, endPos - 1);
+}
+
+void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) {
+ if (len < 4) {
+ printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
+ return;
+ }
+ if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) {
+ printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ if (sysex[2] == SYSEX_MDL_D50) {
+ printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ } else if (sysex[2] != SYSEX_MDL_MT32) {
+ printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4);
+}
+
+void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) {
+ if (device > 0x10) {
+ // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels
+ printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device);
+ return;
+ }
+ // This is checked early in the real devices (before any sysex length checks or further processing)
+ // FIXME: Response to SYSEX_CMD_DAT reset with partials active (and in general) is untested.
+ if ((command == SYSEX_CMD_DT1 || command == SYSEX_CMD_DAT) && sysex[0] == 0x7F) {
+ reset();
+ return;
+ }
+ if (len < 4) {
+ printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
+ return;
+ }
+ unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
+ if (checksum != sysex[len - 1]) {
+ printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
+ return;
+ }
+ len -= 1; // Exclude checksum
+ switch (command) {
+ case SYSEX_CMD_DAT:
+ if (hasActivePartials()) {
+ printDebug("playSysexWithoutHeader: Got SYSEX_CMD_DAT but partials are active - ignoring");
+ // FIXME: We should send SYSEX_CMD_RJC in this case
+ break;
+ }
+ // Deliberate fall-through
+ case SYSEX_CMD_DT1:
+ writeSysex(device, sysex, len);
+ break;
+ case SYSEX_CMD_RQD:
+ if (hasActivePartials()) {
+ printDebug("playSysexWithoutHeader: Got SYSEX_CMD_RQD but partials are active - ignoring");
+ // FIXME: We should send SYSEX_CMD_RJC in this case
+ break;
+ }
+ // Deliberate fall-through
+ case SYSEX_CMD_RQ1:
+ readSysex(device, sysex, len);
+ break;
+ default:
+ printDebug("playSysexWithoutHeader: Unsupported command %02x", command);
+ return;
+ }
+}
+
+void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) const {
+ // NYI
+}
+
+void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
+ Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
+ addr = MT32EMU_MEMADDR(addr);
+ sysex += 3;
+ len -= 3;
+ //printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr));
+ // NOTE: Please keep both lower and upper bounds in each check, for ease of reading
+
+ // Process channel-specific sysex by converting it to device-global
+ if (device < 0x10) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
+#endif
+ if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
+ int offset;
+ if (chantable[device] == -1) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" (Channel not mapped to a part... 0 offset)");
+#endif
+ offset = 0;
+ } else if (chantable[device] == 8) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" (Channel mapped to rhythm... 0 offset)");
+#endif
+ offset = 0;
+ } else {
+ offset = chantable[device] * sizeof(MemParams::PatchTemp);
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" (Setting extra offset to %d)", offset);
+#endif
+ }
+ addr += MT32EMU_MEMADDR(0x030000) + offset;
+ } else if (/*addr >= MT32EMU_MEMADDR(0x010000) && */ addr < MT32EMU_MEMADDR(0x020000)) {
+ addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
+ } else if (/*addr >= MT32EMU_MEMADDR(0x020000) && */ addr < MT32EMU_MEMADDR(0x030000)) {
+ int offset;
+ if (chantable[device] == -1) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" (Channel not mapped to a part... 0 offset)");
+#endif
+ offset = 0;
+ } else if (chantable[device] == 8) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" (Channel mapped to rhythm... 0 offset)");
+#endif
+ offset = 0;
+ } else {
+ offset = chantable[device] * sizeof(TimbreParam);
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" (Setting extra offset to %d)", offset);
+#endif
+ }
+ addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset;
+ } else {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" Invalid channel");
+#endif
+ return;
+ }
+ }
+
+ // Process device-global sysex (possibly converted from channel-specific sysex above)
+ for (;;) {
+ // Find the appropriate memory region
+ const MemoryRegion *region = findMemoryRegion(addr);
+
+ if (region == NULL) {
+ printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len);
+ break;
+ }
+ writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex);
+
+ Bit32u next = region->next(addr, len);
+ if (next == 0) {
+ break;
+ }
+ addr += next;
+ sysex += next;
+ len -= next;
+ }
+}
+
+void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) {
+ const MemoryRegion *region = findMemoryRegion(addr);
+ if (region != NULL) {
+ readMemoryRegion(region, addr, len, data);
+ }
+}
+
+void Synth::initMemoryRegions() {
+ // Timbre max tables are slightly more complicated than the others, which are used directly from the ROM.
+ // The ROM (sensibly) just has maximums for TimbreParam.commonParam followed by just one TimbreParam.partialParam,
+ // so we produce a table with all partialParams filled out, as well as padding for PaddedTimbre, for quick lookup.
+ paddedTimbreMaxTable = new Bit8u[sizeof(MemParams::PaddedTimbre)];
+ memcpy(&paddedTimbreMaxTable[0], &controlROMData[controlROMMap->timbreMaxTable], sizeof(TimbreParam::CommonParam) + sizeof(TimbreParam::PartialParam)); // commonParam and one partialParam
+ int pos = sizeof(TimbreParam::CommonParam) + sizeof(TimbreParam::PartialParam);
+ for (int i = 0; i < 3; i++) {
+ memcpy(&paddedTimbreMaxTable[pos], &controlROMData[controlROMMap->timbreMaxTable + sizeof(TimbreParam::CommonParam)], sizeof(TimbreParam::PartialParam));
+ pos += sizeof(TimbreParam::PartialParam);
+ }
+ memset(&paddedTimbreMaxTable[pos], 0, 10); // Padding
+ patchTempMemoryRegion = new PatchTempMemoryRegion(this, (Bit8u *)&mt32ram.patchTemp[0], &controlROMData[controlROMMap->patchMaxTable]);
+ rhythmTempMemoryRegion = new RhythmTempMemoryRegion(this, (Bit8u *)&mt32ram.rhythmTemp[0], &controlROMData[controlROMMap->rhythmMaxTable]);
+ timbreTempMemoryRegion = new TimbreTempMemoryRegion(this, (Bit8u *)&mt32ram.timbreTemp[0], paddedTimbreMaxTable);
+ patchesMemoryRegion = new PatchesMemoryRegion(this, (Bit8u *)&mt32ram.patches[0], &controlROMData[controlROMMap->patchMaxTable]);
+ timbresMemoryRegion = new TimbresMemoryRegion(this, (Bit8u *)&mt32ram.timbres[0], paddedTimbreMaxTable);
+ systemMemoryRegion = new SystemMemoryRegion(this, (Bit8u *)&mt32ram.system, &controlROMData[controlROMMap->systemMaxTable]);
+ displayMemoryRegion = new DisplayMemoryRegion(this);
+ resetMemoryRegion = new ResetMemoryRegion(this);
+}
+
+void Synth::deleteMemoryRegions() {
+ delete patchTempMemoryRegion;
+ patchTempMemoryRegion = NULL;
+ delete rhythmTempMemoryRegion;
+ rhythmTempMemoryRegion = NULL;
+ delete timbreTempMemoryRegion;
+ timbreTempMemoryRegion = NULL;
+ delete patchesMemoryRegion;
+ patchesMemoryRegion = NULL;
+ delete timbresMemoryRegion;
+ timbresMemoryRegion = NULL;
+ delete systemMemoryRegion;
+ systemMemoryRegion = NULL;
+ delete displayMemoryRegion;
+ displayMemoryRegion = NULL;
+ delete resetMemoryRegion;
+ resetMemoryRegion = NULL;
+
+ delete[] paddedTimbreMaxTable;
+ paddedTimbreMaxTable = NULL;
+}
+
+MemoryRegion *Synth::findMemoryRegion(Bit32u addr) {
+ MemoryRegion *regions[] = {
+ patchTempMemoryRegion,
+ rhythmTempMemoryRegion,
+ timbreTempMemoryRegion,
+ patchesMemoryRegion,
+ timbresMemoryRegion,
+ systemMemoryRegion,
+ displayMemoryRegion,
+ resetMemoryRegion,
+ NULL
+ };
+ for (int pos = 0; regions[pos] != NULL; pos++) {
+ if (regions[pos]->contains(addr)) {
+ return regions[pos];
+ }
+ }
+ return NULL;
+}
+
+void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data) {
+ unsigned int first = region->firstTouched(addr);
+ //unsigned int last = region->lastTouched(addr, len);
+ unsigned int off = region->firstTouchedOffset(addr);
+ len = region->getClampedLen(addr, len);
+
+ unsigned int m;
+
+ if (region->isReadable()) {
+ region->read(first, off, data, len);
+ } else {
+ // FIXME: We might want to do these properly in future
+ for (m = 0; m < len; m += 2) {
+ data[m] = 0xff;
+ if (m + 1 < len) {
+ data[m+1] = (Bit8u)region->type;
+ }
+ }
+ }
+}
+
+void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data) {
+ unsigned int first = region->firstTouched(addr);
+ unsigned int last = region->lastTouched(addr, len);
+ unsigned int off = region->firstTouchedOffset(addr);
+ switch (region->type) {
+ case MR_PatchTemp:
+ region->write(first, off, data, len);
+ //printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);
+
+ for (unsigned int i = first; i <= last; i++) {
+ int absTimbreNum = mt32ram.patchTemp[i].patch.timbreGroup * 64 + mt32ram.patchTemp[i].patch.timbreNum;
+ char timbreName[11];
+ memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10);
+ timbreName[10] = 0;
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s), outlevel=%d", first, last, off, off + len, i, absTimbreNum, timbreName, mt32ram.patchTemp[i].outputLevel);
+#endif
+ if (parts[i] != NULL) {
+ if (i != 8) {
+ // Note: Confirmed on CM-64 that we definitely *should* update the timbre here,
+ // but only in the case that the sysex actually writes to those values
+ if (i == first && off > 2) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" (Not updating timbre, since those values weren't touched)");
+#endif
+ } else {
+ parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
+ }
+ }
+ parts[i]->refresh();
+ }
+ }
+ break;
+ case MR_RhythmTemp:
+ region->write(first, off, data, len);
+ for (unsigned int i = first; i <= last; i++) {
+ int timbreNum = mt32ram.rhythmTemp[i].timbre;
+ char timbreName[11];
+ if (timbreNum < 94) {
+ memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10);
+ timbreName[10] = 0;
+ } else {
+ strcpy(timbreName, "[None]");
+ }
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", first, last, off, off + len, i, mt32ram.rhythmTemp[i].outputLevel, mt32ram.rhythmTemp[i].panpot, mt32ram.rhythmTemp[i].reverbSwitch, mt32ram.rhythmTemp[i].timbre, timbreName);
+#endif
+ }
+ if (parts[8] != NULL) {
+ parts[8]->refresh();
+ }
+ break;
+ case MR_TimbreTemp:
+ region->write(first, off, data, len);
+ for (unsigned int i = first; i <= last; i++) {
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.timbreTemp[i].common.name, 10);
+ instrumentName[10] = 0;
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", first, last, off, off + len, i, instrumentName);
+#endif
+ if (parts[i] != NULL) {
+ parts[i]->refresh();
+ }
+ }
+ break;
+ case MR_Patches:
+ region->write(first, off, data, len);
+#if MT32EMU_MONITOR_SYSEX > 0
+ for (unsigned int i = first; i <= last; i++) {
+ PatchParam *patch = &mt32ram.patches[i];
+ int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10);
+ instrumentName[10] = 0;
+ Bit8u *n = (Bit8u *)patch;
+ printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", first, last, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]);
+ }
+#endif
+ break;
+ case MR_Timbres:
+ // Timbres
+ first += 128;
+ last += 128;
+ region->write(first, off, data, len);
+ for (unsigned int i = first; i <= last; i++) {
+#if MT32EMU_MONITOR_TIMBRES >= 1
+ TimbreParam *timbre = &mt32ram.timbres[i].timbre;
+ char instrumentName[11];
+ memcpy(instrumentName, timbre->common.name, 10);
+ instrumentName[10] = 0;
+ printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", first, last, off, off + len, i, instrumentName);
+#if MT32EMU_MONITOR_TIMBRES >= 2
+#define DT(x) printDebug(" " #x ": %d", timbre->x)
+ DT(common.partialStructure12);
+ DT(common.partialStructure34);
+ DT(common.partialMute);
+ DT(common.noSustain);
+
+#define DTP(x) \
+ DT(partial[x].wg.pitchCoarse); \
+ DT(partial[x].wg.pitchFine); \
+ DT(partial[x].wg.pitchKeyfollow); \
+ DT(partial[x].wg.pitchBenderEnabled); \
+ DT(partial[x].wg.waveform); \
+ DT(partial[x].wg.pcmWave); \
+ DT(partial[x].wg.pulseWidth); \
+ DT(partial[x].wg.pulseWidthVeloSensitivity); \
+ DT(partial[x].pitchEnv.depth); \
+ DT(partial[x].pitchEnv.veloSensitivity); \
+ DT(partial[x].pitchEnv.timeKeyfollow); \
+ DT(partial[x].pitchEnv.time[0]); \
+ DT(partial[x].pitchEnv.time[1]); \
+ DT(partial[x].pitchEnv.time[2]); \
+ DT(partial[x].pitchEnv.time[3]); \
+ DT(partial[x].pitchEnv.level[0]); \
+ DT(partial[x].pitchEnv.level[1]); \
+ DT(partial[x].pitchEnv.level[2]); \
+ DT(partial[x].pitchEnv.level[3]); \
+ DT(partial[x].pitchEnv.level[4]); \
+ DT(partial[x].pitchLFO.rate); \
+ DT(partial[x].pitchLFO.depth); \
+ DT(partial[x].pitchLFO.modSensitivity); \
+ DT(partial[x].tvf.cutoff); \
+ DT(partial[x].tvf.resonance); \
+ DT(partial[x].tvf.keyfollow); \
+ DT(partial[x].tvf.biasPoint); \
+ DT(partial[x].tvf.biasLevel); \
+ DT(partial[x].tvf.envDepth); \
+ DT(partial[x].tvf.envVeloSensitivity); \
+ DT(partial[x].tvf.envDepthKeyfollow); \
+ DT(partial[x].tvf.envTimeKeyfollow); \
+ DT(partial[x].tvf.envTime[0]); \
+ DT(partial[x].tvf.envTime[1]); \
+ DT(partial[x].tvf.envTime[2]); \
+ DT(partial[x].tvf.envTime[3]); \
+ DT(partial[x].tvf.envTime[4]); \
+ DT(partial[x].tvf.envLevel[0]); \
+ DT(partial[x].tvf.envLevel[1]); \
+ DT(partial[x].tvf.envLevel[2]); \
+ DT(partial[x].tvf.envLevel[3]); \
+ DT(partial[x].tva.level); \
+ DT(partial[x].tva.veloSensitivity); \
+ DT(partial[x].tva.biasPoint1); \
+ DT(partial[x].tva.biasLevel1); \
+ DT(partial[x].tva.biasPoint2); \
+ DT(partial[x].tva.biasLevel2); \
+ DT(partial[x].tva.envTimeKeyfollow); \
+ DT(partial[x].tva.envTimeVeloSensitivity); \
+ DT(partial[x].tva.envTime[0]); \
+ DT(partial[x].tva.envTime[1]); \
+ DT(partial[x].tva.envTime[2]); \
+ DT(partial[x].tva.envTime[3]); \
+ DT(partial[x].tva.envTime[4]); \
+ DT(partial[x].tva.envLevel[0]); \
+ DT(partial[x].tva.envLevel[1]); \
+ DT(partial[x].tva.envLevel[2]); \
+ DT(partial[x].tva.envLevel[3]);
+
+ DTP(0);
+ DTP(1);
+ DTP(2);
+ DTP(3);
+#undef DTP
+#undef DT
+#endif
+#endif
+ // FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)...
+ // Does the real MT-32 automatically do this?
+ for (unsigned int part = 0; part < 9; part++) {
+ if (parts[part] != NULL) {
+ parts[part]->refreshTimbre(i);
+ }
+ }
+ }
+ break;
+ case MR_System:
+ region->write(0, off, data, len);
+
+ report(ReportType_devReconfig, NULL);
+ // FIXME: We haven't properly confirmed any of this behaviour
+ // In particular, we tend to reset things such as reverb even if the write contained
+ // the same parameters as were already set, which may be wrong.
+ // On the other hand, the real thing could be resetting things even when they aren't touched
+ // by the write at all.
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("WRITE-SYSTEM:");
+#endif
+ if (off <= SYSTEM_MASTER_TUNE_OFF && off + len > SYSTEM_MASTER_TUNE_OFF) {
+ refreshSystemMasterTune();
+ }
+ if (off <= SYSTEM_REVERB_LEVEL_OFF && off + len > SYSTEM_REVERB_MODE_OFF) {
+ refreshSystemReverbParameters();
+ }
+ if (off <= SYSTEM_RESERVE_SETTINGS_END_OFF && off + len > SYSTEM_RESERVE_SETTINGS_START_OFF) {
+ refreshSystemReserveSettings();
+ }
+ if (off <= SYSTEM_CHAN_ASSIGN_END_OFF && off + len > SYSTEM_CHAN_ASSIGN_START_OFF) {
+ int firstPart = off - SYSTEM_CHAN_ASSIGN_START_OFF;
+ if(firstPart < 0)
+ firstPart = 0;
+ int lastPart = off + len - SYSTEM_CHAN_ASSIGN_START_OFF;
+ if(lastPart > 9)
+ lastPart = 9;
+ refreshSystemChanAssign(firstPart, lastPart);
+ }
+ if (off <= SYSTEM_MASTER_VOL_OFF && off + len > SYSTEM_MASTER_VOL_OFF) {
+ refreshSystemMasterVol();
+ }
+ break;
+ case MR_Display:
+ char buf[MAX_SYSEX_SIZE];
+ memcpy(&buf, &data[0], len);
+ buf[len] = 0;
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("WRITE-LCD: %s", buf);
+#endif
+ report(ReportType_lcdMessage, buf);
+ break;
+ case MR_Reset:
+ reset();
+ break;
+ }
+}
+
+void Synth::refreshSystemMasterTune() {
+#if MT32EMU_MONITOR_SYSEX > 0
+ //FIXME:KG: This is just an educated guess.
+ // The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here)
+ // The MT-32 documentation claims a range of 432.1Hz-457.6Hz
+ float masterTune = 440.0f * EXP2F((mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f));
+ printDebug(" Master Tune: %f", masterTune);
+#endif
+}
+
+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 MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" (Reverb overridden - ignoring)");
+#endif
+ return;
+ }
+ report(ReportType_newReverbMode, &mt32ram.system.reverbMode);
+ report(ReportType_newReverbTime, &mt32ram.system.reverbTime);
+ report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel);
+
+ ReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode];
+#if MT32EMU_REDUCE_REVERB_MEMORY
+ if (reverbModel != newReverbModel) {
+ if (reverbModel != NULL) {
+ reverbModel->close();
+ }
+ newReverbModel->open(myProp.sampleRate);
+ }
+#endif
+ reverbModel = newReverbModel;
+ reverbModel->setParameters(mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
+}
+
+void Synth::refreshSystemReserveSettings() {
+ Bit8u *rset = mt32ram.system.reserveSettings;
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
+#endif
+ partialManager->setReserve(rset);
+}
+
+void Synth::refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart) {
+ memset(chantable, -1, sizeof(chantable));
+
+ // CONFIRMED: In the case of assigning a channel to multiple parts, the lower part wins.
+ for (unsigned int i = 0; i <= 8; i++) {
+ if (parts[i] != NULL && i >= firstPart && i <= lastPart) {
+ // CONFIRMED: Decay is started for all polys, and all controllers are reset, for every part whose assignment was touched by the sysex write.
+ parts[i]->allSoundOff();
+ parts[i]->resetAllControllers();
+ }
+ int chan = mt32ram.system.chanAssign[i];
+ if (chan != 16 && chantable[chan] == -1) {
+ chantable[chan] = i;
+ }
+ }
+
+#if MT32EMU_MONITOR_SYSEX > 0
+ Bit8u *rset = mt32ram.system.chanAssign;
+ printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
+#endif
+}
+
+void Synth::refreshSystemMasterVol() {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug(" Master volume: %d", mt32ram.system.masterVol);
+#endif
+}
+
+void Synth::refreshSystem() {
+ refreshSystemMasterTune();
+ refreshSystemReverbParameters();
+ refreshSystemReserveSettings();
+ refreshSystemChanAssign(0, 8);
+ refreshSystemMasterVol();
+}
+
+void Synth::reset() {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("RESET");
+#endif
+ report(ReportType_devReset, NULL);
+ partialManager->deactivateAll();
+ mt32ram = mt32default;
+ for (int i = 0; i < 9; i++) {
+ parts[i]->reset();
+ if (i != 8) {
+ parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
+ } else {
+ parts[8]->refresh();
+ }
+ }
+ refreshSystem();
+ isEnabled = false;
+}
+
+void Synth::render(Bit16s *stream, Bit32u len) {
+ if (!isEnabled) {
+ memset(stream, 0, len * sizeof(Bit16s) * 2);
+ return;
+ }
+ 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++) {
+ stream[0] = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]);
+ stream[1] = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]);
+ stream += 2;
+ }
+ len -= thisLen;
+ }
+}
+
+bool Synth::prerender() {
+ int newPrerenderWriteIx = (prerenderWriteIx + 1) % MAX_PRERENDER_SAMPLES;
+ if (newPrerenderWriteIx == prerenderReadIx) {
+ // The prerender buffer is full
+ return false;
+ }
+ doRenderStreams(
+ prerenderNonReverbLeft + prerenderWriteIx,
+ prerenderNonReverbRight + prerenderWriteIx,
+ prerenderReverbDryLeft + prerenderWriteIx,
+ prerenderReverbDryRight + prerenderWriteIx,
+ prerenderReverbWetLeft + prerenderWriteIx,
+ prerenderReverbWetRight + prerenderWriteIx,
+ 1);
+ prerenderWriteIx = newPrerenderWriteIx;
+ return true;
+}
+
+static inline void maybeCopy(Bit16s *out, Bit32u outPos, Bit16s *in, Bit32u inPos, Bit32u len) {
+ if (out == NULL) {
+ return;
+ }
+ memcpy(out + outPos, in + inPos, len * sizeof(Bit16s));
+}
+
+void Synth::copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len) {
+ maybeCopy(nonReverbLeft, pos, prerenderNonReverbLeft, prerenderReadIx, len);
+ maybeCopy(nonReverbRight, pos, prerenderNonReverbRight, prerenderReadIx, len);
+ maybeCopy(reverbDryLeft, pos, prerenderReverbDryLeft, prerenderReadIx, len);
+ maybeCopy(reverbDryRight, pos, prerenderReverbDryRight, prerenderReadIx, len);
+ maybeCopy(reverbWetLeft, pos, prerenderReverbWetLeft, prerenderReadIx, len);
+ maybeCopy(reverbWetRight, pos, prerenderReverbWetRight, prerenderReadIx, len);
+}
+
+void Synth::checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len) {
+ if (prerenderReadIx > prerenderWriteIx) {
+ // There's data in the prerender buffer, and the write index has wrapped.
+ Bit32u prerenderCopyLen = MAX_PRERENDER_SAMPLES - prerenderReadIx;
+ if (prerenderCopyLen > len) {
+ prerenderCopyLen = len;
+ }
+ copyPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, prerenderCopyLen);
+ len -= prerenderCopyLen;
+ pos += prerenderCopyLen;
+ prerenderReadIx = (prerenderReadIx + prerenderCopyLen) % MAX_PRERENDER_SAMPLES;
+ }
+ if (prerenderReadIx < prerenderWriteIx) {
+ // There's data in the prerender buffer, and the write index is ahead of the read index.
+ Bit32u prerenderCopyLen = prerenderWriteIx - prerenderReadIx;
+ if (prerenderCopyLen > len) {
+ prerenderCopyLen = len;
+ }
+ copyPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, prerenderCopyLen);
+ len -= prerenderCopyLen;
+ pos += prerenderCopyLen;
+ prerenderReadIx += prerenderCopyLen;
+ }
+ if (prerenderReadIx == prerenderWriteIx) {
+ // If the ring buffer's empty, reset it to start at 0 to minimise wrapping,
+ // which requires two writes instead of one.
+ prerenderReadIx = prerenderWriteIx = 0;
+ }
+}
+
+void Synth::renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len) {
+ if (!isEnabled) {
+ clearIfNonNull(nonReverbLeft, len);
+ clearIfNonNull(nonReverbRight, len);
+ clearIfNonNull(reverbDryLeft, len);
+ clearIfNonNull(reverbDryRight, len);
+ clearIfNonNull(reverbWetLeft, len);
+ clearIfNonNull(reverbWetRight, len);
+ return;
+ }
+ Bit32u pos = 0;
+
+ // First, check for data in the prerender buffer and spit that out before generating anything new.
+ // Note that the prerender buffer is rarely used - see comments elsewhere for details.
+ checkPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, len);
+
+ while (len > 0) {
+ Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
+ doRenderStreams(
+ streamOffset(nonReverbLeft, pos),
+ streamOffset(nonReverbRight, pos),
+ streamOffset(reverbDryLeft, pos),
+ streamOffset(reverbDryRight, pos),
+ streamOffset(reverbWetLeft, pos),
+ streamOffset(reverbWetRight, pos),
+ thisLen);
+ len -= thisLen;
+ pos += thisLen;
+ }
+}
+
+// FIXME: Using more temporary buffers than we need to
+void Synth::doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len) {
+ clearFloats(&tmpBufMixLeft[0], &tmpBufMixRight[0], len);
+ if (!reverbEnabled) {
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
+ mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
+ mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
+ }
+ }
+ if (nonReverbLeft != NULL) {
+ la32FloatToBit16sFunc(nonReverbLeft, &tmpBufMixLeft[0], len, outputGain);
+ }
+ if (nonReverbRight != NULL) {
+ la32FloatToBit16sFunc(nonReverbRight, &tmpBufMixRight[0], len, outputGain);
+ }
+ clearIfNonNull(reverbDryLeft, len);
+ clearIfNonNull(reverbDryRight, len);
+ clearIfNonNull(reverbWetLeft, len);
+ clearIfNonNull(reverbWetRight, len);
+ } else {
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (!partialManager->shouldReverb(i)) {
+ if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
+ mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
+ mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
+ }
+ }
+ }
+ if (nonReverbLeft != NULL) {
+ la32FloatToBit16sFunc(nonReverbLeft, &tmpBufMixLeft[0], len, outputGain);
+ }
+ if (nonReverbRight != NULL) {
+ la32FloatToBit16sFunc(nonReverbRight, &tmpBufMixRight[0], len, outputGain);
+ }
+
+ clearFloats(&tmpBufMixLeft[0], &tmpBufMixRight[0], len);
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialManager->shouldReverb(i)) {
+ if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
+ mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
+ mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
+ }
+ }
+ }
+ if (reverbDryLeft != NULL) {
+ la32FloatToBit16sFunc(reverbDryLeft, &tmpBufMixLeft[0], len, outputGain);
+ }
+ if (reverbDryRight != NULL) {
+ la32FloatToBit16sFunc(reverbDryRight, &tmpBufMixRight[0], len, outputGain);
+ }
+
+ // FIXME: Note that on the real devices, reverb input and output are signed linear 16-bit (well, kinda, there's some fudging) PCM, not float.
+ reverbModel->process(&tmpBufMixLeft[0], &tmpBufMixRight[0], &tmpBufReverbOutLeft[0], &tmpBufReverbOutRight[0], len);
+ if (reverbWetLeft != NULL) {
+ reverbFloatToBit16sFunc(reverbWetLeft, &tmpBufReverbOutLeft[0], len, reverbOutputGain);
+ }
+ if (reverbWetRight != NULL) {
+ reverbFloatToBit16sFunc(reverbWetRight, &tmpBufReverbOutRight[0], len, reverbOutputGain);
+ }
+ }
+ partialManager->clearAlreadyOutputed();
+ renderedSampleCount += len;
+}
+
+void Synth::printPartialUsage(unsigned long sampleOffset) {
+ unsigned int partialUsage[9];
+ partialManager->getPerPartPartialUsage(partialUsage);
+ if (sampleOffset > 0) {
+ printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->getFreePartialCount());
+ } else {
+ printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->getFreePartialCount());
+ }
+}
+
+bool Synth::hasActivePartials() const {
+ if (prerenderReadIx != prerenderWriteIx) {
+ // Data in the prerender buffer means that the current isActive() states are "in the future".
+ // It also means that partials are definitely active at this render point.
+ return true;
+ }
+ for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) {
+ if (partialManager->getPartial(partialNum)->isActive()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Synth::isActive() const {
+ if (hasActivePartials()) {
+ return true;
+ }
+ if (reverbEnabled) {
+ return reverbModel->isActive();
+ }
+ return false;
+}
+
+const Partial *Synth::getPartial(unsigned int partialNum) const {
+ return partialManager->getPartial(partialNum);
+}
+
+const Part *Synth::getPart(unsigned int partNum) const {
+ if (partNum > 8) {
+ return NULL;
+ }
+ return parts[partNum];
+}
+
+void MemoryRegion::read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const {
+ off += entry * entrySize;
+ // This method should never be called with out-of-bounds parameters,
+ // or on an unsupported region - seeing any of this debug output indicates a bug in the emulator
+ if (off > entrySize * entries - 1) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ synth->printDebug("read[%d]: parameters start out of bounds: entry=%d, off=%d, len=%d", type, entry, off, len);
+#endif
+ return;
+ }
+ if (off + len > entrySize * entries) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ synth->printDebug("read[%d]: parameters end out of bounds: entry=%d, off=%d, len=%d", type, entry, off, len);
+#endif
+ len = entrySize * entries - off;
+ }
+ Bit8u *src = getRealMemory();
+ if (src == NULL) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ synth->printDebug("read[%d]: unreadable region: entry=%d, off=%d, len=%d", type, entry, off, len);
+#endif
+ return;
+ }
+ memcpy(dst, src + off, len);
+}
+
+void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init) const {
+ unsigned int memOff = entry * entrySize + off;
+ // This method should never be called with out-of-bounds parameters,
+ // or on an unsupported region - seeing any of this debug output indicates a bug in the emulator
+ if (off > entrySize * entries - 1) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ synth->printDebug("write[%d]: parameters start out of bounds: entry=%d, off=%d, len=%d", type, entry, off, len);
+#endif
+ return;
+ }
+ if (off + len > entrySize * entries) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ synth->printDebug("write[%d]: parameters end out of bounds: entry=%d, off=%d, len=%d", type, entry, off, len);
+#endif
+ len = entrySize * entries - off;
+ }
+ Bit8u *dest = getRealMemory();
+ if (dest == NULL) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ synth->printDebug("write[%d]: unwritable region: entry=%d, off=%d, len=%d", type, entry, off, len);
+#endif
+ }
+
+ for (unsigned int i = 0; i < len; i++) {
+ Bit8u desiredValue = src[i];
+ Bit8u maxValue = getMaxValue(memOff);
+ // maxValue == 0 means write-protected unless called from initialisation code, in which case it really means the maximum value is 0.
+ if (maxValue != 0 || init) {
+ if (desiredValue > maxValue) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ synth->printDebug("write[%d]: Wanted 0x%02x at %d, but max 0x%02x", type, desiredValue, memOff, maxValue);
+#endif
+ desiredValue = maxValue;
+ }
+ dest[memOff] = desiredValue;
+ } else if (desiredValue != 0) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ // Only output debug info if they wanted to write non-zero, since a lot of things cause this to spit out a lot of debug info otherwise.
+ synth->printDebug("write[%d]: Wanted 0x%02x at %d, but write-protected", type, desiredValue, memOff);
+#endif
+ }
+ memOff++;
+ }
+}
+
+}
diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h
new file mode 100644
index 0000000000..ccabce7282
--- /dev/null
+++ b/audio/softsynth/mt32/Synth.h
@@ -0,0 +1,471 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_SYNTH_H
+#define MT32EMU_SYNTH_H
+
+//#include <cstdarg>
+
+namespace MT32Emu {
+
+class TableInitialiser;
+class Partial;
+class PartialManager;
+class Part;
+
+/**
+ * Methods for emulating the connection between the LA32 and the DAC, which involves
+ * some hacks in the real devices for doubling the volume.
+ * See also http://en.wikipedia.org/wiki/Roland_MT-32#Digital_overflow
+ */
+enum DACInputMode {
+ // Produces samples at double the volume, without tricks.
+ // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
+ // * Higher quality than the real devices
+ DACInputMode_NICE,
+
+ // Produces samples that exactly match the bits output from the emulated LA32.
+ // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
+ // * Much less likely to overdrive than any other mode.
+ // * Half the volume of any of the other modes, meaning its volume relative to the reverb
+ // output when mixed together directly will sound wrong.
+ // * Perfect for developers while debugging :)
+ DACInputMode_PURE,
+
+ // Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia).
+ // Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low):
+ // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX
+ DACInputMode_GENERATION1,
+
+ // Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG).
+ // Bit order at DAC (where each number represents the original LA32 output bit number):
+ // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14
+ DACInputMode_GENERATION2
+};
+
+enum ReportType {
+ // Errors
+ ReportType_errorControlROM = 1,
+ ReportType_errorPCMROM,
+ ReportType_errorSampleRate,
+
+ // Progress
+ ReportType_progressInit,
+
+ // HW spec
+ ReportType_availableSSE,
+ ReportType_available3DNow,
+ ReportType_usingSSE,
+ ReportType_using3DNow,
+
+ // General info
+ ReportType_lcdMessage,
+ ReportType_devReset,
+ ReportType_devReconfig,
+ ReportType_newReverbMode,
+ ReportType_newReverbTime,
+ ReportType_newReverbLevel
+};
+
+enum LoadResult {
+ LoadResult_OK,
+ LoadResult_NotFound,
+ LoadResult_Unreadable,
+ LoadResult_Invalid
+};
+
+struct SynthProperties {
+ // Sample rate to use in mixing
+ unsigned int sampleRate;
+
+ // Deprecated - ignored. Use Synth::setReverbEnabled() instead.
+ bool useReverb;
+ // Deprecated - ignored. Use Synth::setReverbOverridden() instead.
+ bool useDefaultReverb;
+ // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead.
+ unsigned char reverbType;
+ // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead.
+ unsigned char reverbTime;
+ // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead.
+ unsigned char reverbLevel;
+ // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash)
+ // Not used if "openFile" is set. May be NULL in any case.
+ const char *baseDir;
+ // This is used as the first argument to all callbacks
+ void *userData;
+ // Callback for reporting various errors and information. May be NULL
+ int (*report)(void *userData, ReportType type, const void *reportData);
+ // Callback for debug messages, in vprintf() format
+ void (*printDebug)(void *userData, const char *fmt, va_list list);
+ // Callback for providing an implementation of File, opened and ready for use
+ // May be NULL, in which case a default implementation will be used.
+ Common::File *(*openFile)(void *userData, const char *filename);
+ // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted.
+ void (*closeFile)(void *userData, Common::File *file);
+};
+
+// This is the specification of the Callback routine used when calling the RecalcWaveforms
+// function
+typedef void (*recalcStatusCallback)(int percDone);
+
+typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain);
+
+const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
+
+const Bit8u SYSEX_MDL_MT32 = 0x16;
+const Bit8u SYSEX_MDL_D50 = 0x14;
+
+const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1
+const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1
+const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data
+const Bit8u SYSEX_CMD_RQD = 0x41; // Request data
+const Bit8u SYSEX_CMD_DAT = 0x42; // Data set
+const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge
+const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
+const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
+const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
+
+const int MAX_SYSEX_SIZE = 512;
+
+const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
+
+struct ControlROMPCMStruct {
+ Bit8u pos;
+ Bit8u len;
+ Bit8u pitchLSB;
+ Bit8u pitchMSB;
+};
+
+struct ControlROMMap {
+ Bit16u idPos;
+ Bit16u idLen;
+ const char *idBytes;
+ Bit16u pcmTable; // 4 * pcmCount bytes
+ Bit16u pcmCount;
+ Bit16u timbreAMap; // 128 bytes
+ Bit16u timbreAOffset;
+ bool timbreACompressed;
+ Bit16u timbreBMap; // 128 bytes
+ Bit16u timbreBOffset;
+ bool timbreBCompressed;
+ Bit16u timbreRMap; // 2 * timbreRCount bytes
+ Bit16u timbreRCount;
+ Bit16u rhythmSettings; // 4 * rhythmSettingsCount bytes
+ Bit16u rhythmSettingsCount;
+ Bit16u reserveSettings; // 9 bytes
+ Bit16u panSettings; // 8 bytes
+ Bit16u programSettings; // 8 bytes
+ Bit16u rhythmMaxTable; // 4 bytes
+ Bit16u patchMaxTable; // 16 bytes
+ Bit16u systemMaxTable; // 23 bytes
+ Bit16u timbreMaxTable; // 72 bytes
+};
+
+enum MemoryRegionType {
+ MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
+};
+
+class MemoryRegion {
+private:
+ Synth *synth;
+ Bit8u *realMemory;
+ Bit8u *maxTable;
+public:
+ MemoryRegionType type;
+ Bit32u startAddr, entrySize, entries;
+
+ MemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable, MemoryRegionType useType, Bit32u useStartAddr, Bit32u useEntrySize, Bit32u useEntries) {
+ synth = useSynth;
+ realMemory = useRealMemory;
+ maxTable = useMaxTable;
+ type = useType;
+ startAddr = useStartAddr;
+ entrySize = useEntrySize;
+ entries = useEntries;
+ }
+ int lastTouched(Bit32u addr, Bit32u len) const {
+ return (offset(addr) + len - 1) / entrySize;
+ }
+ int firstTouchedOffset(Bit32u addr) const {
+ return offset(addr) % entrySize;
+ }
+ int firstTouched(Bit32u addr) const {
+ return offset(addr) / entrySize;
+ }
+ Bit32u regionEnd() const {
+ return startAddr + entrySize * entries;
+ }
+ bool contains(Bit32u addr) const {
+ return addr >= startAddr && addr < regionEnd();
+ }
+ int offset(Bit32u addr) const {
+ return addr - startAddr;
+ }
+ Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
+ if (addr + len > regionEnd())
+ return regionEnd() - addr;
+ return len;
+ }
+ Bit32u next(Bit32u addr, Bit32u len) const {
+ if (addr + len > regionEnd()) {
+ return regionEnd() - addr;
+ }
+ return 0;
+ }
+ Bit8u getMaxValue(int off) const {
+ if (maxTable == NULL)
+ return 0xFF;
+ return maxTable[off % entrySize];
+ }
+ Bit8u *getRealMemory() const {
+ return realMemory;
+ }
+ bool isReadable() const {
+ return getRealMemory() != NULL;
+ }
+ void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const;
+ void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
+};
+
+class PatchTempMemoryRegion : public MemoryRegion {
+public:
+ PatchTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9) {}
+};
+class RhythmTempMemoryRegion : public MemoryRegion {
+public:
+ RhythmTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85) {}
+};
+class TimbreTempMemoryRegion : public MemoryRegion {
+public:
+ TimbreTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8) {}
+};
+class PatchesMemoryRegion : public MemoryRegion {
+public:
+ PatchesMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128) {}
+};
+class TimbresMemoryRegion : public MemoryRegion {
+public:
+ TimbresMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64) {}
+};
+class SystemMemoryRegion : public MemoryRegion {
+public:
+ SystemMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::System), 1) {}
+};
+class DisplayMemoryRegion : public MemoryRegion {
+public:
+ DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {}
+};
+class ResetMemoryRegion : public MemoryRegion {
+public:
+ ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
+};
+
+class ReverbModel {
+public:
+ virtual ~ReverbModel() {}
+ // After construction or a close(), open() will be called at least once before any other call (with the exception of close()).
+ virtual void open(unsigned int sampleRate) = 0;
+ // May be called multiple times without an open() in between.
+ virtual void close() = 0;
+ virtual void setParameters(Bit8u time, Bit8u level) = 0;
+ virtual void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) = 0;
+ virtual bool isActive() const = 0;
+};
+
+class Synth {
+friend class Part;
+friend class RhythmPart;
+friend class Poly;
+friend class Partial;
+friend class Tables;
+friend class MemoryRegion;
+friend class TVA;
+friend class TVF;
+friend class TVP;
+private:
+ PatchTempMemoryRegion *patchTempMemoryRegion;
+ RhythmTempMemoryRegion *rhythmTempMemoryRegion;
+ TimbreTempMemoryRegion *timbreTempMemoryRegion;
+ PatchesMemoryRegion *patchesMemoryRegion;
+ TimbresMemoryRegion *timbresMemoryRegion;
+ SystemMemoryRegion *systemMemoryRegion;
+ DisplayMemoryRegion *displayMemoryRegion;
+ ResetMemoryRegion *resetMemoryRegion;
+
+ Bit8u *paddedTimbreMaxTable;
+
+ bool isEnabled;
+
+ PCMWaveEntry *pcmWaves; // Array
+
+ const ControlROMMap *controlROMMap;
+ Bit8u controlROMData[CONTROL_ROM_SIZE];
+ float *pcmROMData;
+ int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
+
+ Bit8s chantable[32];
+
+ Bit32u renderedSampleCount;
+
+ Tables tables;
+
+ MemParams mt32ram, mt32default;
+
+ ReverbModel *reverbModels[4];
+ ReverbModel *reverbModel;
+ bool reverbEnabled;
+ bool reverbOverridden;
+
+ FloatToBit16sFunc la32FloatToBit16sFunc;
+ FloatToBit16sFunc reverbFloatToBit16sFunc;
+ float outputGain;
+ float reverbOutputGain;
+
+ bool isOpen;
+
+ PartialManager *partialManager;
+ Part *parts[9];
+
+ // FIXME: We can reorganise things so that we don't need all these separate tmpBuf, tmp and prerender buffers.
+ // This should be rationalised when things have stabilised a bit (if prerender buffers don't die in the mean time).
+
+ float tmpBufPartialLeft[MAX_SAMPLES_PER_RUN];
+ float tmpBufPartialRight[MAX_SAMPLES_PER_RUN];
+ float tmpBufMixLeft[MAX_SAMPLES_PER_RUN];
+ float tmpBufMixRight[MAX_SAMPLES_PER_RUN];
+ float tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN];
+ float tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN];
+
+ Bit16s tmpNonReverbLeft[MAX_SAMPLES_PER_RUN];
+ Bit16s tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
+ Bit16s tmpReverbDryLeft[MAX_SAMPLES_PER_RUN];
+ Bit16s tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
+ Bit16s tmpReverbWetLeft[MAX_SAMPLES_PER_RUN];
+ Bit16s tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
+
+ // These ring buffers are only used to simulate delays present on the real device.
+ // In particular, when a partial needs to be aborted to free it up for use by a new Poly,
+ // the controller will busy-loop waiting for the sound to finish.
+ Bit16s prerenderNonReverbLeft[MAX_PRERENDER_SAMPLES];
+ Bit16s prerenderNonReverbRight[MAX_PRERENDER_SAMPLES];
+ Bit16s prerenderReverbDryLeft[MAX_PRERENDER_SAMPLES];
+ Bit16s prerenderReverbDryRight[MAX_PRERENDER_SAMPLES];
+ Bit16s prerenderReverbWetLeft[MAX_PRERENDER_SAMPLES];
+ Bit16s prerenderReverbWetRight[MAX_PRERENDER_SAMPLES];
+ int prerenderReadIx;
+ int prerenderWriteIx;
+
+ SynthProperties myProp;
+
+ bool prerender();
+ void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len);
+ void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len);
+ void doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
+
+ void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+ void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const;
+ void initMemoryRegions();
+ void deleteMemoryRegions();
+ MemoryRegion *findMemoryRegion(Bit32u addr);
+ void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data);
+ void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data);
+
+ LoadResult loadControlROM(const char *filename);
+ LoadResult loadPCMROM(const char *filename);
+
+ bool initPCMList(Bit16u mapAddress, Bit16u count);
+ bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed);
+ bool initCompressedTimbre(int drumNum, const Bit8u *mem, unsigned int memLen);
+
+ void refreshSystemMasterTune();
+ void refreshSystemReverbParameters();
+ void refreshSystemReserveSettings();
+ void refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart);
+ void refreshSystemMasterVol();
+ void refreshSystem();
+ void reset();
+
+ unsigned int getSampleRate() const;
+
+ void printPartialUsage(unsigned long sampleOffset = 0);
+protected:
+ int report(ReportType type, const void *reportData);
+ Common::File *openFile(const char *filename);
+ void closeFile(Common::File *file);
+ void printDebug(const char *fmt, ...);
+
+public:
+ static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
+
+ Synth();
+ ~Synth();
+
+ // Used to initialise the MT-32. Must be called before any other function.
+ // Returns true if initialization was sucessful, otherwise returns false.
+ bool open(SynthProperties &useProp);
+
+ // Closes the MT-32 and deallocates any memory used by the synthesizer
+ void close(void);
+
+ // Sends a 4-byte MIDI message to the MT-32 for immediate playback
+ void playMsg(Bit32u msg);
+ void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
+
+ // Sends a string of Sysex commands to the MT-32 for immediate interpretation
+ // The length is in bytes
+ void playSysex(const Bit8u *sysex, Bit32u len);
+ void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
+ void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len);
+ void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+
+ void setReverbEnabled(bool reverbEnabled);
+ bool isReverbEnabled() const;
+ void setReverbOverridden(bool reverbOverridden);
+ bool isReverbOverridden() const;
+ void setDACInputMode(DACInputMode mode);
+
+ // Sets output gain factor. Applied to all output samples and unrelated with the synth's Master volume.
+ void setOutputGain(float);
+
+ // Sets output gain factor for the reverb wet output. setOutputGain() doesn't change reverb output gain.
+ void setReverbOutputGain(float);
+
+ // Renders samples to the specified output stream.
+ // The length is in frames, not bytes (in 16-bit stereo,
+ // one frame is 4 bytes).
+ void render(Bit16s *stream, Bit32u len);
+
+ // Renders samples to the specified output streams (any or all of which may be NULL).
+ void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
+
+ // Returns true when there is at least one active partial, otherwise false.
+ bool hasActivePartials() const;
+
+ // Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active.
+ bool isActive() const;
+
+ const Partial *getPartial(unsigned int partialNum) const;
+
+ void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
+
+ // partNum should be 0..7 for Part 1..8, or 8 for Rhythm
+ const Part *getPart(unsigned int partNum) const;
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp
new file mode 100644
index 0000000000..c3be6db591
--- /dev/null
+++ b/audio/softsynth/mt32/TVA.cpp
@@ -0,0 +1,365 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This class emulates the calculations performed by the 8095 microcontroller in order to configure the LA-32's amplitude ramp for a single partial at each stage of its TVA envelope.
+ * Unless we introduced bugs, it should be pretty much 100% accurate according to Mok's specifications.
+*/
+//#include <cmath>
+
+#include "mt32emu.h"
+#include "mmath.h"
+
+namespace MT32Emu {
+
+// CONFIRMED: Matches a table in ROM - haven't got around to coming up with a formula for it yet.
+static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0};
+
+TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) :
+ partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system) {
+}
+
+void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
+ target = newTarget;
+ phase = newPhase;
+ ampRamp->startRamp(newTarget, newIncrement);
+#if MT32EMU_MONITOR_TVA >= 1
+ partial->getSynth()->printDebug("[+%lu] [Partial %d] TVA,ramp,%d,%d,%d,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), (newIncrement & 0x80) ? -1 : 1, (newIncrement & 0x7F), newPhase);
+#endif
+}
+
+void TVA::end(int newPhase) {
+ phase = newPhase;
+ playing = false;
+#if MT32EMU_MONITOR_TVA >= 1
+ partial->getSynth()->printDebug("[+%lu] [Partial %d] TVA,end,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), newPhase);
+#endif
+}
+
+static int multBias(Bit8u biasLevel, int bias) {
+ return (bias * biasLevelToAmpSubtractionCoeff[biasLevel]) >> 5;
+}
+
+static int calcBiasAmpSubtraction(Bit8u biasPoint, Bit8u biasLevel, int key) {
+ if ((biasPoint & 0x40) == 0) {
+ int bias = biasPoint + 33 - key;
+ if (bias > 0) {
+ return multBias(biasLevel, bias);
+ }
+ } else {
+ int bias = biasPoint - 31 - key;
+ if (bias < 0) {
+ bias = -bias;
+ return multBias(biasLevel, bias);
+ }
+ }
+ return 0;
+}
+
+static int calcBiasAmpSubtractions(const TimbreParam::PartialParam *partialParam, int key) {
+ int biasAmpSubtraction1 = calcBiasAmpSubtraction(partialParam->tva.biasPoint1, partialParam->tva.biasLevel1, key);
+ if (biasAmpSubtraction1 > 255) {
+ return 255;
+ }
+ int biasAmpSubtraction2 = calcBiasAmpSubtraction(partialParam->tva.biasPoint2, partialParam->tva.biasLevel2, key);
+ if (biasAmpSubtraction2 > 255) {
+ return 255;
+ }
+ int biasAmpSubtraction = biasAmpSubtraction1 + biasAmpSubtraction2;
+ if (biasAmpSubtraction > 255) {
+ return 255;
+ }
+ return biasAmpSubtraction;
+}
+
+static int calcVeloAmpSubtraction(Bit8u veloSensitivity, unsigned int velocity) {
+ // FIXME:KG: Better variable names
+ int velocityMult = veloSensitivity - 50;
+ int absVelocityMult = velocityMult < 0 ? -velocityMult : velocityMult;
+ velocityMult = (signed)((unsigned)(velocityMult * ((signed)velocity - 64)) << 2);
+ return absVelocityMult - (velocityMult >> 8); // PORTABILITY NOTE: Assumes arithmetic shift
+}
+
+static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system_, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) {
+ int amp = 155;
+
+ if (!partial->isRingModulatingSlave()) {
+ amp -= tables->masterVolToAmpSubtraction[system_->masterVol];
+ if (amp < 0) {
+ return 0;
+ }
+ amp -= tables->levelToAmpSubtraction[patchTemp->outputLevel];
+ if (amp < 0) {
+ return 0;
+ }
+ amp -= tables->levelToAmpSubtraction[expression];
+ if (amp < 0) {
+ return 0;
+ }
+ if (rhythmTemp != NULL) {
+ amp -= tables->levelToAmpSubtraction[rhythmTemp->outputLevel];
+ if (amp < 0) {
+ return 0;
+ }
+ }
+ }
+ amp -= biasAmpSubtraction;
+ if (amp < 0) {
+ return 0;
+ }
+ amp -= tables->levelToAmpSubtraction[partialParam->tva.level];
+ if (amp < 0) {
+ return 0;
+ }
+ amp -= veloAmpSubtraction;
+ if (amp < 0) {
+ return 0;
+ }
+ if (amp > 155) {
+ amp = 155;
+ }
+ amp -= partialParam->tvf.resonance >> 1;
+ if (amp < 0) {
+ return 0;
+ }
+ return amp;
+}
+
+int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) {
+ if (envTimeKeyfollow == 0) {
+ return 0;
+ }
+ return (key - 60) >> (5 - envTimeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift
+}
+
+void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartialParam, const MemParams::RhythmTemp *newRhythmTemp) {
+ part = newPart;
+ partialParam = newPartialParam;
+ patchTemp = newPart->getPatchTemp();
+ rhythmTemp = newRhythmTemp;
+
+ playing = true;
+
+ Tables *tables = &partial->getSynth()->tables;
+
+ int key = partial->getPoly()->getKey();
+ int velocity = partial->getPoly()->getVelocity();
+
+ keyTimeSubtraction = calcKeyTimeSubtraction(partialParam->tva.envTimeKeyfollow, key);
+
+ biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key);
+ veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity);
+
+ int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+ int newPhase;
+ if (partialParam->tva.envTime[0] == 0) {
+ // Initially go to the TVA_PHASE_ATTACK target amp, and spend the next phase going from there to the TVA_PHASE_2 target amp
+ // Note that this means that velocity never affects time for this partial.
+ newTarget += partialParam->tva.envLevel[0];
+ newPhase = TVA_PHASE_ATTACK; // The first target used in nextPhase() will be TVA_PHASE_2
+ } else {
+ // Initially go to the base amp determined by TVA level, part volume, etc., and spend the next phase going from there to the full TVA_PHASE_ATTACK target amp.
+ newPhase = TVA_PHASE_BASIC; // The first target used in nextPhase() will be TVA_PHASE_ATTACK
+ }
+
+ ampRamp->reset();//currentAmp = 0;
+
+ // "Go downward as quickly as possible".
+ // Since the current value is 0, the LA32Ramp will notice that we're already at or below the target and trying to go downward,
+ // and therefore jump to the target immediately and raise an interrupt.
+ startRamp((Bit8u)newTarget, 0x80 | 127, newPhase);
+}
+
+void TVA::startAbort() {
+ startRamp(64, 0x80 | 127, TVA_PHASE_RELEASE);
+}
+
+void TVA::startDecay() {
+ if (phase >= TVA_PHASE_RELEASE) {
+ return;
+ }
+ Bit8u newIncrement;
+ if (partialParam->tva.envTime[4] == 0) {
+ newIncrement = 1;
+ } else {
+ newIncrement = -partialParam->tva.envTime[4];
+ }
+ // The next time nextPhase() is called, it will think TVA_PHASE_RELEASE has finished and the partial will be aborted
+ startRamp(0, newIncrement, TVA_PHASE_RELEASE);
+}
+
+void TVA::handleInterrupt() {
+ nextPhase();
+}
+
+void TVA::recalcSustain() {
+ // We get pinged periodically by the pitch code to recalculate our values when in sustain.
+ // This is done so that the TVA will respond to things like MIDI expression and volume changes while it's sustaining, which it otherwise wouldn't do.
+
+ // The check for envLevel[3] == 0 strikes me as slightly dumb. FIXME: Explain why
+ if (phase != TVA_PHASE_SUSTAIN || partialParam->tva.envLevel[3] == 0) {
+ return;
+ }
+ // We're sustaining. Recalculate all the values
+ Tables *tables = &partial->getSynth()->tables;
+ int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+ newTarget += partialParam->tva.envLevel[3];
+ // Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp.
+ int targetDelta = newTarget - target;
+
+ // Calculate an increment to get to the new amp value in a short, more or less consistent amount of time
+ Bit8u newIncrement;
+ if (targetDelta >= 0) {
+ newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - 2;
+ } else {
+ newIncrement = (tables->envLogarithmicTime[(Bit8u)-targetDelta] - 2) | 0x80;
+ }
+ // Configure so that once the transition's complete and nextPhase() is called, we'll just re-enter sustain phase (or decay phase, depending on parameters at the time).
+ startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1);
+}
+
+bool TVA::isPlaying() const {
+ return playing;
+}
+
+int TVA::getPhase() const {
+ return phase;
+}
+
+void TVA::nextPhase() {
+ Tables *tables = &partial->getSynth()->tables;
+
+ if (phase >= TVA_PHASE_DEAD || !playing) {
+ partial->getSynth()->printDebug("TVA::nextPhase(): Shouldn't have got here with phase %d, playing=%s", phase, playing ? "true" : "false");
+ return;
+ }
+ int newPhase = phase + 1;
+
+ if (newPhase == TVA_PHASE_DEAD) {
+ end(newPhase);
+ return;
+ }
+
+ bool allLevelsZeroFromNowOn = false;
+ if (partialParam->tva.envLevel[3] == 0) {
+ if (newPhase == TVA_PHASE_4) {
+ allLevelsZeroFromNowOn = true;
+ } else if (partialParam->tva.envLevel[2] == 0) {
+ if (newPhase == TVA_PHASE_3) {
+ allLevelsZeroFromNowOn = true;
+ } else if (partialParam->tva.envLevel[1] == 0) {
+ if (newPhase == TVA_PHASE_2) {
+ allLevelsZeroFromNowOn = true;
+ } else if (partialParam->tva.envLevel[0] == 0) {
+ if (newPhase == TVA_PHASE_ATTACK) { // this line added, missing in ROM - FIXME: Add description of repercussions
+ allLevelsZeroFromNowOn = true;
+ }
+ }
+ }
+ }
+ }
+
+ int newTarget;
+ int newIncrement;
+ int envPointIndex = phase;
+
+ if (!allLevelsZeroFromNowOn) {
+ newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+
+ if (newPhase == TVA_PHASE_SUSTAIN || newPhase == TVA_PHASE_RELEASE) {
+ if (partialParam->tva.envLevel[3] == 0) {
+ end(newPhase);
+ return;
+ }
+ if (!partial->getPoly()->canSustain()) {
+ newPhase = TVA_PHASE_RELEASE;
+ newTarget = 0;
+ newIncrement = -partialParam->tva.envTime[4];
+ if (newIncrement == 0) {
+ // We can't let the increment be 0, or there would be no emulated interrupt.
+ // So we do an "upward" increment, which should set the amp to 0 extremely quickly
+ // and cause an "interrupt" to bring us back to nextPhase().
+ newIncrement = 1;
+ }
+ } else {
+ newTarget += partialParam->tva.envLevel[3];
+ newIncrement = 0;
+ }
+ } else {
+ newTarget += partialParam->tva.envLevel[envPointIndex];
+ }
+ } else {
+ newTarget = 0;
+ }
+
+ if ((newPhase != TVA_PHASE_SUSTAIN && newPhase != TVA_PHASE_RELEASE) || allLevelsZeroFromNowOn) {
+ int envTimeSetting = partialParam->tva.envTime[envPointIndex];
+
+ if (newPhase == TVA_PHASE_ATTACK) {
+ envTimeSetting -= ((signed)partial->getPoly()->getVelocity() - 64) >> (6 - partialParam->tva.envTimeVeloSensitivity); // PORTABILITY NOTE: Assumes arithmetic shift
+
+ if (envTimeSetting <= 0 && partialParam->tva.envTime[envPointIndex] != 0) {
+ envTimeSetting = 1;
+ }
+ } else {
+ envTimeSetting -= keyTimeSubtraction;
+ }
+ if (envTimeSetting > 0) {
+ int targetDelta = newTarget - target;
+ if (targetDelta <= 0) {
+ if (targetDelta == 0) {
+ // target and newTarget are the same.
+ // We can't have an increment of 0 or we wouldn't get an emulated interrupt.
+ // So instead make the target one less than it really should be and set targetDelta accordingly.
+ targetDelta = -1;
+ newTarget--;
+ if (newTarget < 0) {
+ // Oops, newTarget is less than zero now, so let's do it the other way:
+ // Make newTarget one more than it really should've been and set targetDelta accordingly.
+ // FIXME (apparent bug in real firmware):
+ // This means targetDelta will be positive just below here where it's inverted, and we'll end up using envLogarithmicTime[-1], and we'll be setting newIncrement to be descending later on, etc..
+ targetDelta = 1;
+ newTarget = -newTarget;
+ }
+ }
+ targetDelta = -targetDelta;
+ newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting;
+ if (newIncrement <= 0) {
+ newIncrement = 1;
+ }
+ newIncrement = newIncrement | 0x80;
+ } else {
+ // FIXME: The last 22 or so entries in this table are 128 - surely that fucks things up, since that ends up being -128 signed?
+ newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting;
+ if (newIncrement <= 0) {
+ newIncrement = 1;
+ }
+ }
+ } else {
+ newIncrement = newTarget >= target ? (0x80 | 127) : 127;
+ }
+
+ // FIXME: What's the point of this? It's checked or set to non-zero everywhere above
+ if (newIncrement == 0) {
+ newIncrement = 1;
+ }
+ }
+
+ startRamp((Bit8u)newTarget, (Bit8u)newIncrement, newPhase);
+}
+
+}
diff --git a/audio/softsynth/mt32/TVA.h b/audio/softsynth/mt32/TVA.h
new file mode 100644
index 0000000000..a104fe4c1f
--- /dev/null
+++ b/audio/softsynth/mt32/TVA.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_TVA_H
+#define MT32EMU_TVA_H
+
+namespace MT32Emu {
+
+class Part;
+
+// Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to
+// newPhase's value.
+enum {
+ // In this phase, the base amp (as calculated in calcBasicAmp()) is targeted with an instant time.
+ // This phase is entered by reset() only if time[0] != 0.
+ TVA_PHASE_BASIC = 0,
+
+ // In this phase, level[0] is targeted within time[0], and velocity potentially affects time
+ TVA_PHASE_ATTACK = 1,
+
+ // In this phase, level[1] is targeted within time[1]
+ TVA_PHASE_2 = 2,
+
+ // In this phase, level[2] is targeted within time[2]
+ TVA_PHASE_3 = 3,
+
+ // In this phase, level[3] is targeted within time[3]
+ TVA_PHASE_4 = 4,
+
+ // In this phase, immediately goes to PHASE_RELEASE unless the poly is set to sustain.
+ // Aborts the partial if level[3] is 0.
+ // Otherwise level[3] is continued, no phase change will occur until some external influence (like pedal release)
+ TVA_PHASE_SUSTAIN = 5,
+
+ // In this phase, 0 is targeted within time[4] (the time calculation is quite different from the other phases)
+ TVA_PHASE_RELEASE = 6,
+
+ // It's PHASE_DEAD, Jim.
+ TVA_PHASE_DEAD = 7
+};
+
+class TVA {
+private:
+ const Partial * const partial;
+ LA32Ramp *ampRamp;
+ const MemParams::System * const system_;
+
+ const Part *part;
+ const TimbreParam::PartialParam *partialParam;
+ const MemParams::PatchTemp *patchTemp;
+ const MemParams::RhythmTemp *rhythmTemp;
+
+ bool playing;
+
+ int biasAmpSubtraction;
+ int veloAmpSubtraction;
+ int keyTimeSubtraction;
+
+ Bit8u target;
+ int phase;
+
+ void startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase);
+ void end(int newPhase);
+ void nextPhase();
+
+public:
+ TVA(const Partial *partial, LA32Ramp *ampRamp);
+ void reset(const Part *part, const TimbreParam::PartialParam *partialParam, const MemParams::RhythmTemp *rhythmTemp);
+ void handleInterrupt();
+ void recalcSustain();
+ void startDecay();
+ void startAbort();
+
+ bool isPlaying() const;
+ int getPhase() const;
+};
+
+}
+
+#endif /* TVA_H_ */
diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp
new file mode 100644
index 0000000000..58f72e5a9b
--- /dev/null
+++ b/audio/softsynth/mt32/TVF.cpp
@@ -0,0 +1,230 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cmath>
+
+#include "mt32emu.h"
+#include "mmath.h"
+
+namespace MT32Emu {
+
+// Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to
+// newPhase's value.
+enum {
+ // When this is the target phase, level[0] is targeted within time[0]
+ // Note that this phase is always set up in reset(), not nextPhase()
+ PHASE_ATTACK = 1,
+
+ // When this is the target phase, level[1] is targeted within time[1]
+ PHASE_2 = 2,
+
+ // When this is the target phase, level[2] is targeted within time[2]
+ PHASE_3 = 3,
+
+ // When this is the target phase, level[3] is targeted within time[3]
+ PHASE_4 = 4,
+
+ // When this is the target phase, immediately goes to PHASE_RELEASE unless the poly is set to sustain.
+ // Otherwise level[3] is continued with increment 0 - no phase change will occur until some external influence (like pedal release)
+ PHASE_SUSTAIN = 5,
+
+ // 0 is targeted within time[4] (the time calculation is quite different from the other phases)
+ PHASE_RELEASE = 6,
+
+ // 0 is targeted with increment 0 (thus theoretically staying that way forever)
+ PHASE_DONE = 7
+};
+
+static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u basePitch, unsigned int key) {
+ // This table matches the values used by a real LAPC-I.
+ static const Bit8s biasLevelToBiasMult[] = {85, 42, 21, 16, 10, 5, 2, 0, -2, -5, -10, -16, -21, -74, -85};
+ // These values represent unique options with no consistent pattern, so we have to use something like a table in any case.
+ // The table entries, when divided by 21, match approximately what the manual claims:
+ // -1, -1/2, -1/4, 0, 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, 1, 5/4, 3/2, 2, s1, s2
+ // Note that the entry for 1/8 is rounded to 2 (from 1/8 * 21 = 2.625), which seems strangely inaccurate compared to the others.
+ static const Bit8s keyfollowMult21[] = {-21, -10, -5, 0, 2, 5, 8, 10, 13, 16, 18, 21, 26, 32, 42, 21, 21};
+ int baseCutoff = keyfollowMult21[partialParam->tvf.keyfollow] - keyfollowMult21[partialParam->wg.pitchKeyfollow];
+ // baseCutoff range now: -63 to 63
+ baseCutoff *= (int)key - 60;
+ // baseCutoff range now: -3024 to 3024
+ int biasPoint = partialParam->tvf.biasPoint;
+ if ((biasPoint & 0x40) == 0) {
+ // biasPoint range here: 0 to 63
+ int bias = biasPoint + 33 - key; // bias range here: -75 to 84
+ if (bias > 0) {
+ bias = -bias; // bias range here: -1 to -84
+ baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -7140 to 7140
+ // baseCutoff range now: -10164 to 10164
+ }
+ } else {
+ // biasPoint range here: 64 to 127
+ int bias = biasPoint - 31 - key; // bias range here: -75 to 84
+ if (bias < 0) {
+ baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: −6375 to 6375
+ // baseCutoff range now: -9399 to 9399
+ }
+ }
+ // baseCutoff range now: -10164 to 10164
+ baseCutoff += ((partialParam->tvf.cutoff << 4) - 800);
+ // baseCutoff range now: -10964 to 10964
+ if (baseCutoff >= 0) {
+ // FIXME: Potentially bad if baseCutoff ends up below -2056?
+ int pitchDeltaThing = (basePitch >> 4) + baseCutoff - 3584;
+ if (pitchDeltaThing > 0) {
+ baseCutoff -= pitchDeltaThing;
+ }
+ } else if (baseCutoff < -2048) {
+ baseCutoff = -2048;
+ }
+ baseCutoff += 2056;
+ baseCutoff >>= 4; // PORTABILITY NOTE: Hmm... Depends whether it could've been below -2056, but maybe arithmetic shift assumed?
+ if (baseCutoff > 255) {
+ baseCutoff = 255;
+ }
+ return (Bit8u)baseCutoff;
+}
+
+TVF::TVF(const Partial *usePartial, LA32Ramp *useCutoffModifierRamp) :
+ partial(usePartial), cutoffModifierRamp(useCutoffModifierRamp) {
+}
+
+void TVF::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
+ target = newTarget;
+ phase = newPhase;
+ cutoffModifierRamp->startRamp(newTarget, newIncrement);
+#if MT32EMU_MONITOR_TVF >= 1
+ partial->getSynth()->printDebug("[+%lu] [Partial %d] TVF,ramp,%d,%d,%d,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), newTarget, (newIncrement & 0x80) ? -1 : 1, (newIncrement & 0x7F), newPhase);
+#endif
+}
+
+void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int basePitch) {
+ partialParam = newPartialParam;
+
+ unsigned int key = partial->getPoly()->getKey();
+ unsigned int velocity = partial->getPoly()->getVelocity();
+
+ Tables *tables = &partial->getSynth()->tables;
+
+ baseCutoff = calcBaseCutoff(newPartialParam, basePitch, key);
+#if MT32EMU_MONITOR_TVF >= 1
+ partial->getSynth()->printDebug("[+%lu] [Partial %d] TVF,base,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), baseCutoff);
+#endif
+
+ int newLevelMult = velocity * newPartialParam->tvf.envVeloSensitivity;
+ newLevelMult >>= 6;
+ newLevelMult += 109 - newPartialParam->tvf.envVeloSensitivity;
+ newLevelMult += ((signed)key - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow);
+ if (newLevelMult < 0) {
+ newLevelMult = 0;
+ }
+ newLevelMult *= newPartialParam->tvf.envDepth;
+ newLevelMult >>= 6;
+ if (newLevelMult > 255) {
+ newLevelMult = 255;
+ }
+ levelMult = newLevelMult;
+
+ if (newPartialParam->tvf.envTimeKeyfollow != 0) {
+ keyTimeSubtraction = ((signed)key - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow);
+ } else {
+ keyTimeSubtraction = 0;
+ }
+
+ int newTarget = (newLevelMult * newPartialParam->tvf.envLevel[0]) >> 8;
+ int envTimeSetting = newPartialParam->tvf.envTime[0] - keyTimeSubtraction;
+ int newIncrement;
+ if (envTimeSetting <= 0) {
+ newIncrement = (0x80 | 127);
+ } else {
+ newIncrement = tables->envLogarithmicTime[newTarget] - envTimeSetting;
+ if (newIncrement <= 0) {
+ newIncrement = 1;
+ }
+ }
+ cutoffModifierRamp->reset();
+ startRamp(newTarget, newIncrement, PHASE_2 - 1);
+}
+
+Bit8u TVF::getBaseCutoff() const {
+ return baseCutoff;
+}
+
+void TVF::handleInterrupt() {
+ nextPhase();
+}
+
+void TVF::startDecay() {
+ if (phase >= PHASE_RELEASE) {
+ return;
+ }
+ if (partialParam->tvf.envTime[4] == 0) {
+ startRamp(0, 1, PHASE_DONE - 1);
+ } else {
+ startRamp(0, -partialParam->tvf.envTime[4], PHASE_DONE - 1);
+ }
+}
+
+void TVF::nextPhase() {
+ Tables *tables = &partial->getSynth()->tables;
+ int newPhase = phase + 1;
+
+ switch (newPhase) {
+ case PHASE_DONE:
+ startRamp(0, 0, newPhase);
+ return;
+ case PHASE_SUSTAIN:
+ case PHASE_RELEASE:
+ // FIXME: Afaict newPhase should never be PHASE_RELEASE here. And if it were, this is an odd way to handle it.
+ if (!partial->getPoly()->canSustain()) {
+ phase = newPhase; // FIXME: Correct?
+ startDecay(); // FIXME: This should actually start decay even if phase is already 6. Does that matter?
+ return;
+ }
+ startRamp((levelMult * partialParam->tvf.envLevel[3]) >> 8, 0, newPhase);
+ return;
+ }
+
+ int envPointIndex = phase;
+ int envTimeSetting = partialParam->tvf.envTime[envPointIndex] - keyTimeSubtraction;
+
+ int newTarget = (levelMult * partialParam->tvf.envLevel[envPointIndex]) >> 8;
+ int newIncrement;
+ if (envTimeSetting > 0) {
+ int targetDelta = newTarget - target;
+ if (targetDelta == 0) {
+ if (newTarget == 0) {
+ targetDelta = 1;
+ newTarget = 1;
+ } else {
+ targetDelta = -1;
+ newTarget--;
+ }
+ }
+ newIncrement = tables->envLogarithmicTime[targetDelta < 0 ? -targetDelta : targetDelta] - envTimeSetting;
+ if (newIncrement <= 0) {
+ newIncrement = 1;
+ }
+ if (targetDelta < 0) {
+ newIncrement |= 0x80;
+ }
+ } else {
+ newIncrement = newTarget >= target ? (0x80 | 127) : 127;
+ }
+ startRamp(newTarget, newIncrement, newPhase);
+}
+
+}
diff --git a/audio/softsynth/mt32/TVF.h b/audio/softsynth/mt32/TVF.h
new file mode 100644
index 0000000000..490d8de504
--- /dev/null
+++ b/audio/softsynth/mt32/TVF.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_TVF_H
+#define MT32EMU_TVF_H
+
+namespace MT32Emu {
+
+class TVF {
+private:
+ const Partial * const partial;
+ LA32Ramp *cutoffModifierRamp;
+ const TimbreParam::PartialParam *partialParam;
+
+ Bit8u baseCutoff;
+ int keyTimeSubtraction;
+ unsigned int levelMult;
+
+ Bit8u target;
+ unsigned int phase;
+
+ void startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase);
+ void nextPhase();
+
+public:
+ TVF(const Partial *partial, LA32Ramp *cutoffModifierRamp);
+ void reset(const TimbreParam::PartialParam *partialParam, Bit32u basePitch);
+ // Returns the base cutoff (without envelope modification).
+ // The base cutoff is calculated when reset() is called and remains static
+ // for the lifetime of the partial.
+ // Barring bugs, the number returned is confirmed accurate
+ // (based on specs from Mok).
+ Bit8u getBaseCutoff() const;
+ void handleInterrupt();
+ void startDecay();
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp
new file mode 100644
index 0000000000..0b339e8d71
--- /dev/null
+++ b/audio/softsynth/mt32/TVP.cpp
@@ -0,0 +1,321 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cmath>
+//#include <cstdlib>
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+// FIXME: Add Explanation
+static Bit16u lowerDurationToDivisor[] = {34078, 37162, 40526, 44194, 48194, 52556, 57312, 62499};
+
+// These values represent unique options with no consistent pattern, so we have to use something like a table in any case.
+// The table matches exactly what the manual claims (when divided by 8192):
+// -1, -1/2, -1/4, 0, 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, 1, 5/4, 3/2, 2, s1, s2
+// ...except for the last two entries, which are supposed to be "1 cent above 1" and "2 cents above 1", respectively. They can only be roughly approximated with this integer math.
+static Bit16s pitchKeyfollowMult[] = {-8192, -4096, -2048, 0, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 10240, 12288, 16384, 8198, 8226};
+
+// Note: Keys < 60 use keyToPitchTable[60 - key], keys >= 60 use keyToPitchTable[key - 60].
+// FIXME: This table could really be shorter, since we never use e.g. key 127.
+static Bit16u keyToPitchTable[] = {
+ 0, 341, 683, 1024, 1365, 1707, 2048, 2389,
+ 2731, 3072, 3413, 3755, 4096, 4437, 4779, 5120,
+ 5461, 5803, 6144, 6485, 6827, 7168, 7509, 7851,
+ 8192, 8533, 8875, 9216, 9557, 9899, 10240, 10581,
+ 10923, 11264, 11605, 11947, 12288, 12629, 12971, 13312,
+ 13653, 13995, 14336, 14677, 15019, 15360, 15701, 16043,
+ 16384, 16725, 17067, 17408, 17749, 18091, 18432, 18773,
+ 19115, 19456, 19797, 20139, 20480, 20821, 21163, 21504,
+ 21845, 22187, 22528, 22869
+};
+
+TVP::TVP(const Partial *usePartial) :
+ partial(usePartial), system_(&usePartial->getSynth()->mt32ram.system) {
+ unsigned int sampleRate = usePartial->getSynth()->myProp.sampleRate;
+ // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary.
+ maxCounter = sampleRate / 4000;
+ // The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing.
+ // This is how much to increment it by every maxCounter samples.
+ processTimerIncrement = 500000 * maxCounter / sampleRate;
+}
+
+static Bit16s keyToPitch(unsigned int key) {
+ // We're using a table to do: return round_to_nearest_or_even((key - 60) * (4096.0 / 12.0))
+ // Banker's rounding is just slightly annoying to do in C++
+ int k = (int)key;
+ Bit16s pitch = keyToPitchTable[abs(k - 60)];
+ return key < 60 ? -pitch : pitch;
+}
+
+static inline Bit32s coarseToPitch(Bit8u coarse) {
+ return (coarse - 36) * 4096 / 12; // One semitone per coarse offset
+}
+
+static inline Bit32s fineToPitch(Bit8u fine) {
+ return (fine - 50) * 4096 / 1200; // One cent per fine offset
+}
+
+static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, unsigned int key) {
+ Bit32s basePitch = keyToPitch(key);
+ basePitch = (basePitch * pitchKeyfollowMult[partialParam->wg.pitchKeyfollow]) >> 13; // PORTABILITY NOTE: Assumes arithmetic shift
+ basePitch += coarseToPitch(partialParam->wg.pitchCoarse);
+ basePitch += fineToPitch(partialParam->wg.pitchFine);
+ // NOTE:Mok: This is done on MT-32, but not LAPC-I:
+ //pitch += coarseToPitch(patchTemp->patch.keyShift + 12);
+ basePitch += fineToPitch(patchTemp->patch.fineTune);
+
+ const ControlROMPCMStruct *controlROMPCMStruct = partial->getControlROMPCMStruct();
+ if (controlROMPCMStruct != NULL) {
+ basePitch += (Bit32s)((((Bit32s)controlROMPCMStruct->pitchMSB) << 8) | (Bit32s)controlROMPCMStruct->pitchLSB);
+ } else {
+ if ((partialParam->wg.waveform & 1) == 0) {
+ basePitch += 37133; // This puts Middle C at around 261.64Hz (assuming no other modifications, masterTune of 64, etc.)
+ } else {
+ // Sawtooth waves are effectively double the frequency of square waves.
+ // Thus we add 4096 less than for square waves here, which results in halving the frequency.
+ basePitch += 33037;
+ }
+ }
+ if (basePitch < 0) {
+ basePitch = 0;
+ }
+ if (basePitch > 59392) {
+ basePitch = 59392;
+ }
+ return (Bit32u)basePitch;
+}
+
+static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) {
+ if (veloSensitivity == 0 || veloSensitivity > 3) {
+ // Note that on CM-32L/LAPC-I veloSensitivity is never > 3, since it's clipped to 3 by the max tables.
+ return 21845; // aka floor(4096 / 12 * 64), aka ~64 semitones
+ }
+ // When velocity is 127, the multiplier is 21845, aka ~64 semitones (regardless of veloSensitivity).
+ // The lower the velocity, the lower the multiplier. The veloSensitivity determines the amount decreased per velocity value.
+ // The minimum multiplier (with velocity 0, veloSensitivity 3) is 170 (~half a semitone).
+ Bit32u veloMult = 32768;
+ veloMult -= (127 - velocity) << (5 + veloSensitivity);
+ veloMult *= 21845;
+ veloMult >>= 15;
+ return veloMult;
+}
+
+static Bit32s calcTargetPitchOffsetWithoutLFO(const TimbreParam::PartialParam *partialParam, int levelIndex, unsigned int velocity) {
+ int veloMult = calcVeloMult(partialParam->pitchEnv.veloSensitivity, velocity);
+ int targetPitchOffsetWithoutLFO = partialParam->pitchEnv.level[levelIndex] - 50;
+ targetPitchOffsetWithoutLFO = (Bit32s)(targetPitchOffsetWithoutLFO * veloMult) >> (16 - partialParam->pitchEnv.depth); // PORTABILITY NOTE: Assumes arithmetic shift
+ return targetPitchOffsetWithoutLFO;
+}
+
+void TVP::reset(const Part *usePart, const TimbreParam::PartialParam *usePartialParam) {
+ part = usePart;
+ partialParam = usePartialParam;
+ patchTemp = part->getPatchTemp();
+
+ unsigned int key = partial->getPoly()->getKey();
+ unsigned int velocity = partial->getPoly()->getVelocity();
+
+ // FIXME: We're using a per-TVP timer instead of a system-wide one for convenience.
+ timeElapsed = 0;
+
+ basePitch = calcBasePitch(partial, partialParam, patchTemp, key);
+ currentPitchOffset = calcTargetPitchOffsetWithoutLFO(partialParam, 0, velocity);
+ targetPitchOffsetWithoutLFO = currentPitchOffset;
+ phase = 0;
+
+ if (partialParam->pitchEnv.timeKeyfollow) {
+ timeKeyfollowSubtraction = (key - 60) >> (5 - partialParam->pitchEnv.timeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift
+ } else {
+ timeKeyfollowSubtraction = 0;
+ }
+ lfoPitchOffset = 0;
+ counter = 0;
+ pitch = basePitch;
+
+ // These don't really need to be initialised, but it aids debugging.
+ pitchOffsetChangePerBigTick = 0;
+ targetPitchOffsetReachedBigTick = 0;
+ shifts = 0;
+}
+
+Bit32u TVP::getBasePitch() const {
+ return basePitch;
+}
+
+void TVP::updatePitch() {
+ Bit32s newPitch = basePitch + currentPitchOffset;
+ if (!partial->isPCM() || (partial->getControlROMPCMStruct()->len & 0x01) == 0) { // FIXME: Use !partial->pcmWaveEntry->unaffectedByMasterTune instead
+ // FIXME: masterTune recalculation doesn't really happen here, and there are various bugs not yet emulated
+ // 171 is ~half a semitone.
+ newPitch += ((system_->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift.
+ }
+ if ((partialParam->wg.pitchBenderEnabled & 1) != 0) {
+ newPitch += part->getPitchBend();
+ }
+ if (newPitch < 0) {
+ newPitch = 0;
+ }
+ if (newPitch > 59392) {
+ newPitch = 59392;
+ }
+ pitch = (Bit16u)newPitch;
+
+ // FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future.
+ partial->tva->recalcSustain();
+}
+
+void TVP::targetPitchOffsetReached() {
+ currentPitchOffset = targetPitchOffsetWithoutLFO + lfoPitchOffset;
+
+ switch (phase) {
+ case 3:
+ case 4:
+ {
+ int newLFOPitchOffset = (part->getModulation() * partialParam->pitchLFO.modSensitivity) >> 7;
+ newLFOPitchOffset = (newLFOPitchOffset + partialParam->pitchLFO.depth) << 1;
+ if (pitchOffsetChangePerBigTick > 0) {
+ // Go in the opposite direction to last time
+ newLFOPitchOffset = -newLFOPitchOffset;
+ }
+ lfoPitchOffset = newLFOPitchOffset;
+ int targetPitchOffset = targetPitchOffsetWithoutLFO + lfoPitchOffset;
+ setupPitchChange(targetPitchOffset, 101 - partialParam->pitchLFO.rate);
+ updatePitch();
+ break;
+ }
+ case 6:
+ updatePitch();
+ break;
+ default:
+ nextPhase();
+ }
+}
+
+void TVP::nextPhase() {
+ phase++;
+ int envIndex = phase == 6 ? 4 : phase;
+
+ targetPitchOffsetWithoutLFO = calcTargetPitchOffsetWithoutLFO(partialParam, envIndex, partial->getPoly()->getVelocity()); // pitch we'll reach at the end
+
+ int changeDuration = partialParam->pitchEnv.time[envIndex - 1];
+ changeDuration -= timeKeyfollowSubtraction;
+ if (changeDuration > 0) {
+ setupPitchChange(targetPitchOffsetWithoutLFO, changeDuration); // changeDuration between 0 and 112 now
+ updatePitch();
+ } else {
+ targetPitchOffsetReached();
+ }
+}
+
+// Shifts val to the left until bit 31 is 1 and returns the number of shifts
+static Bit8u normalise(Bit32u &val) {
+ Bit8u leftShifts;
+ for (leftShifts = 0; leftShifts < 31; leftShifts++) {
+ if ((val & 0x80000000) != 0) {
+ break;
+ }
+ val = val << 1;
+ }
+ return leftShifts;
+}
+
+void TVP::setupPitchChange(int targetPitchOffset, Bit8u changeDuration) {
+ bool negativeDelta = targetPitchOffset < currentPitchOffset;
+ Bit32s pitchOffsetDelta = targetPitchOffset - currentPitchOffset;
+ if (pitchOffsetDelta > 32767 || pitchOffsetDelta < -32768) {
+ pitchOffsetDelta = 32767;
+ }
+ if (negativeDelta) {
+ pitchOffsetDelta = -pitchOffsetDelta;
+ }
+ // We want to maximise the number of bits of the Bit16s "pitchOffsetChangePerBigTick" we use in order to get the best possible precision later
+ Bit32u absPitchOffsetDelta = pitchOffsetDelta << 16;
+ Bit8u normalisationShifts = normalise(absPitchOffsetDelta); // FIXME: Double-check: normalisationShifts is usually between 0 and 15 here, unless the delta is 0, in which case it's 31
+ absPitchOffsetDelta = absPitchOffsetDelta >> 1; // Make room for the sign bit
+
+ changeDuration--; // changeDuration's now between 0 and 111
+ unsigned int upperDuration = changeDuration >> 3; // upperDuration's now between 0 and 13
+ shifts = normalisationShifts + upperDuration + 2;
+ Bit16u divisor = lowerDurationToDivisor[changeDuration & 7];
+ Bit16s newPitchOffsetChangePerBigTick = ((absPitchOffsetDelta & 0xFFFF0000) / divisor) >> 1; // Result now fits within 15 bits. FIXME: Check nothing's getting sign-extended incorrectly
+ if (negativeDelta) {
+ newPitchOffsetChangePerBigTick = -newPitchOffsetChangePerBigTick;
+ }
+ pitchOffsetChangePerBigTick = newPitchOffsetChangePerBigTick;
+
+ int currentBigTick = timeElapsed >> 8;
+ int durationInBigTicks = divisor >> (12 - upperDuration);
+ if (durationInBigTicks > 32767) {
+ durationInBigTicks = 32767;
+ }
+ // The result of the addition may exceed 16 bits, but wrapping is fine and intended here.
+ targetPitchOffsetReachedBigTick = currentBigTick + durationInBigTicks;
+}
+
+void TVP::startDecay() {
+ phase = 5;
+ lfoPitchOffset = 0;
+ targetPitchOffsetReachedBigTick = timeElapsed >> 8; // FIXME: Afaict there's no good reason for this - check
+}
+
+Bit16u TVP::nextPitch() {
+ // FIXME: Write explanation of counter and time increment
+ if (counter == 0) {
+ timeElapsed += processTimerIncrement;
+ timeElapsed = timeElapsed & 0x00FFFFFF;
+ process();
+ }
+ counter = (counter + 1) % maxCounter;
+ return pitch;
+}
+
+void TVP::process() {
+ if (phase == 0) {
+ targetPitchOffsetReached();
+ return;
+ }
+ if (phase == 5) {
+ nextPhase();
+ return;
+ }
+ if (phase > 7) {
+ updatePitch();
+ return;
+ }
+
+ Bit16s negativeBigTicksRemaining = (timeElapsed >> 8) - targetPitchOffsetReachedBigTick;
+ if (negativeBigTicksRemaining >= 0) {
+ // We've reached the time for a phase change
+ targetPitchOffsetReached();
+ return;
+ }
+ // FIXME: Write explanation for this stuff
+ int rightShifts = shifts;
+ if (rightShifts > 13) {
+ rightShifts -= 13;
+ negativeBigTicksRemaining = negativeBigTicksRemaining >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift
+ rightShifts = 13;
+ }
+ int newResult = ((Bit32s)(negativeBigTicksRemaining * pitchOffsetChangePerBigTick)) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift
+ newResult += targetPitchOffsetWithoutLFO + lfoPitchOffset;
+ currentPitchOffset = newResult;
+ updatePitch();
+}
+
+}
diff --git a/audio/softsynth/mt32/TVP.h b/audio/softsynth/mt32/TVP.h
new file mode 100644
index 0000000000..f6f62f8d39
--- /dev/null
+++ b/audio/softsynth/mt32/TVP.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_TVP_H
+#define MT32EMU_TVP_H
+
+namespace MT32Emu {
+
+class TVP {
+private:
+ const Partial * const partial;
+ const MemParams::System * const system_; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm.
+
+ const Part *part;
+ const TimbreParam::PartialParam *partialParam;
+ const MemParams::PatchTemp *patchTemp;
+
+ int maxCounter;
+ int processTimerIncrement;
+ int counter;
+ Bit32u timeElapsed;
+
+ int phase;
+ Bit32u basePitch;
+ Bit32s targetPitchOffsetWithoutLFO;
+ Bit32s currentPitchOffset;
+
+ Bit16s lfoPitchOffset;
+ // In range -12 - 36
+ Bit8s timeKeyfollowSubtraction;
+
+ Bit16s pitchOffsetChangePerBigTick;
+ Bit16u targetPitchOffsetReachedBigTick;
+ unsigned int shifts;
+
+ Bit16u pitch;
+
+ void updatePitch();
+ void setupPitchChange(int targetPitchOffset, Bit8u changeDuration);
+ void targetPitchOffsetReached();
+ void nextPhase();
+ void process();
+public:
+ TVP(const Partial *partial);
+ void reset(const Part *part, const TimbreParam::PartialParam *partialParam);
+ Bit32u getBasePitch() const;
+ Bit16u nextPitch();
+ void startDecay();
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp
new file mode 100644
index 0000000000..c9bd40b7a4
--- /dev/null
+++ b/audio/softsynth/mt32/Tables.cpp
@@ -0,0 +1,119 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cmath>
+//#include <cstdlib>
+//#include <cstring>
+
+#include "mt32emu.h"
+#include "mmath.h"
+
+using namespace MT32Emu;
+
+Tables::Tables() {
+ initialised = false;
+}
+
+void Tables::init() {
+ if (initialised) {
+ return;
+ }
+ initialised = true;
+
+ int lf;
+ for (lf = 0; lf <= 100; lf++) {
+ // CONFIRMED:KG: This matches a ROM table found by Mok
+ float fVal = (2.0f - LOG10F((float)lf + 1.0f)) * 128.0f;
+ int val = (int)(fVal + 1.0);
+ if (val > 255) {
+ val = 255;
+ }
+ levelToAmpSubtraction[lf] = (Bit8u)val;
+ }
+
+ envLogarithmicTime[0] = 64;
+ for (lf = 1; lf <= 255; lf++) {
+ // CONFIRMED:KG: This matches a ROM table found by Mok
+ envLogarithmicTime[lf] = (Bit8u)ceil(64.0f + LOG2F((float)lf) * 8.0f);
+ }
+
+#ifdef EMULATE_LAPC_I // Dummy #ifdef - we'll have runtime emulation mode selection in future.
+ // CONFIRMED: Based on a table found by Mok in the LAPC-I control ROM
+ // Note that this matches the MT-32 table, but with the values clamped to a maximum of 8.
+ memset(masterVolToAmpSubtraction, 8, 71);
+ memset(masterVolToAmpSubtraction + 71, 7, 3);
+ memset(masterVolToAmpSubtraction + 74, 6, 4);
+ memset(masterVolToAmpSubtraction + 78, 5, 3);
+ memset(masterVolToAmpSubtraction + 81, 4, 4);
+ memset(masterVolToAmpSubtraction + 85, 3, 3);
+ memset(masterVolToAmpSubtraction + 88, 2, 4);
+ memset(masterVolToAmpSubtraction + 92, 1, 4);
+ memset(masterVolToAmpSubtraction + 96, 0, 5);
+#else
+ // CONFIRMED: Based on a table found by Mok in the MT-32 control ROM
+ masterVolToAmpSubtraction[0] = 255;
+ for (int masterVol = 1; masterVol <= 100; masterVol++) {
+ masterVolToAmpSubtraction[masterVol] = (int)(106.31 - 16.0f * LOG2F((float)masterVol));
+ }
+#endif
+
+ for (int i = 0; i <= 100; i++) {
+ pulseWidth100To255[i] = (int)(i * 255 / 100.0f + 0.5f);
+ //synth->printDebug("%d: %d", i, pulseWidth100To255[i]);
+ }
+
+ // Ratio of negative segment to wave length
+ for (int i = 0; i < 128; i++) {
+ // Formula determined from sample analysis.
+ float pt = 0.5f / 127.0f * i;
+ pulseLenFactor[i] = (1.241857812f - pt) * pt; // seems to be 2 ^ (5 / 16) = 1.241857812f
+ }
+
+ for (int i = 0; i < 65536; i++) {
+ // Aka (slightly slower): EXP2F(pitchVal / 4096.0f - 16.0f) * 32000.0f
+ pitchToFreq[i] = EXP2F(i / 4096.0f - 1.034215715f);
+ }
+
+ // found from sample analysis
+ for (int i = 0; i < 1024; i++) {
+ cutoffToCosineLen[i] = EXP2F(i / -128.0f);
+ }
+
+ // found from sample analysis
+ for (int i = 0; i < 1024; i++) {
+ cutoffToFilterAmp[i] = EXP2F(-0.125f * (128.0f - i / 8.0f));
+ }
+
+ // found from sample analysis
+ for (int i = 0; i < 32; i++) {
+ resAmpMax[i] = EXP2F(1.0f - (32 - i) / 4.0f);
+ }
+
+ // found from sample analysis
+ resAmpFadeFactor[7] = 1.0f / 8.0f;
+ resAmpFadeFactor[6] = 2.0f / 8.0f;
+ resAmpFadeFactor[5] = 3.0f / 8.0f;
+ resAmpFadeFactor[4] = 5.0f / 8.0f;
+ resAmpFadeFactor[3] = 8.0f / 8.0f;
+ resAmpFadeFactor[2] = 12.0f / 8.0f;
+ resAmpFadeFactor[1] = 16.0f / 8.0f;
+ resAmpFadeFactor[0] = 31.0f / 8.0f;
+
+ for (int i = 0; i < 5120; i++) {
+ sinf10[i] = sin(FLOAT_PI * i / 2048.0f);
+ }
+}
diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h
new file mode 100644
index 0000000000..a2b5ff5d56
--- /dev/null
+++ b/audio/softsynth/mt32/Tables.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_TABLES_H
+#define MT32EMU_TABLES_H
+
+namespace MT32Emu {
+
+const int MIDDLEC = 60;
+
+class Synth;
+
+class Tables {
+ bool initialised;
+
+public:
+ // Constant LUTs
+
+ // CONFIRMED: This is used to convert several parameters to amp-modifying values in the TVA envelope:
+ // - PatchTemp.outputLevel
+ // - RhythmTemp.outlevel
+ // - PartialParam.tva.level
+ // - expression
+ // It's used to determine how much to subtract from the amp envelope's target value
+ Bit8u levelToAmpSubtraction[101];
+
+ // CONFIRMED: ...
+ Bit8u envLogarithmicTime[256];
+
+ // CONFIRMED: ...
+ Bit8u masterVolToAmpSubtraction[101];
+
+ // CONFIRMED:
+ Bit8u pulseWidth100To255[101];
+
+ float pulseLenFactor[128];
+ float pitchToFreq[65536];
+ float cutoffToCosineLen[1024];
+ float cutoffToFilterAmp[1024];
+ float resAmpMax[32];
+ float resAmpFadeFactor[8];
+ float sinf10[5120];
+
+ Tables();
+ void init();
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/freeverb.cpp b/audio/softsynth/mt32/freeverb.cpp
index 67f065c20e..de8f2632cb 100644
--- a/audio/softsynth/mt32/freeverb.cpp
+++ b/audio/softsynth/mt32/freeverb.cpp
@@ -1,245 +1,245 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-// Comb filter implementation
+// Allpass filter implementation
//
-// Written by
+// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
-#include "audio/softsynth/mt32/freeverb.h"
+#include "freeverb.h"
-comb::comb() {
- filterstore = 0;
+allpass::allpass()
+{
bufidx = 0;
}
-void comb::setbuffer(float *buf, int size) {
+void allpass::setbuffer(float *buf, int size)
+{
buffer = buf;
bufsize = size;
}
-void comb::mute() {
- for (int i = 0; i < bufsize; i++)
- buffer[i] = 0;
-}
-
-void comb::setdamp(float val) {
- damp1 = val;
- damp2 = 1 - val;
-}
-
-float comb::getdamp() {
- return damp1;
+void allpass::mute()
+{
+ for (int i=0; i<bufsize; i++)
+ buffer[i]=0;
}
-void comb::setfeedback(float val) {
+void allpass::setfeedback(float val)
+{
feedback = val;
}
-float comb::getfeedback() {
+float allpass::getfeedback()
+{
return feedback;
}
-// Allpass filter implementation
+void allpass::deletebuffer()
+{
+ delete[] buffer;
+ buffer = 0;
+}
+// Comb filter implementation
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
-allpass::allpass() {
+comb::comb()
+{
+ filterstore = 0;
bufidx = 0;
}
-void allpass::setbuffer(float *buf, int size) {
+void comb::setbuffer(float *buf, int size)
+{
buffer = buf;
bufsize = size;
}
-void allpass::mute() {
- for (int i = 0; i < bufsize; i++)
- buffer[i] = 0;
+void comb::mute()
+{
+ for (int i=0; i<bufsize; i++)
+ buffer[i]=0;
+}
+
+void comb::setdamp(float val)
+{
+ damp1 = val;
+ damp2 = 1-val;
+}
+
+float comb::getdamp()
+{
+ return damp1;
}
-void allpass::setfeedback(float val) {
+void comb::setfeedback(float val)
+{
feedback = val;
}
-float allpass::getfeedback() {
+float comb::getfeedback()
+{
return feedback;
}
+void comb::deletebuffer()
+{
+ delete[] buffer;
+ buffer = 0;
+}
// Reverb model implementation
+//
+// Written by Jezar at Dreampoint, June 2000
+// Modifications by Jerome Fisher, 2009, 2011
+// http://www.dreampoint.co.uk
+// This code is public domain
-revmodel::revmodel() {
- // Tie the components to their buffers
- combL[0].setbuffer(bufcombL1,combtuningL1);
- combR[0].setbuffer(bufcombR1,combtuningR1);
- combL[1].setbuffer(bufcombL2,combtuningL2);
- combR[1].setbuffer(bufcombR2,combtuningR2);
- combL[2].setbuffer(bufcombL3,combtuningL3);
- combR[2].setbuffer(bufcombR3,combtuningR3);
- combL[3].setbuffer(bufcombL4,combtuningL4);
- combR[3].setbuffer(bufcombR4,combtuningR4);
- combL[4].setbuffer(bufcombL5,combtuningL5);
- combR[4].setbuffer(bufcombR5,combtuningR5);
- combL[5].setbuffer(bufcombL6,combtuningL6);
- combR[5].setbuffer(bufcombR6,combtuningR6);
- combL[6].setbuffer(bufcombL7,combtuningL7);
- combR[6].setbuffer(bufcombR7,combtuningR7);
- combL[7].setbuffer(bufcombL8,combtuningL8);
- combR[7].setbuffer(bufcombR8,combtuningR8);
- allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
- allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
- allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
- allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
- allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
- allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
- allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
- allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
+revmodel::revmodel(float scaletuning)
+{
+ int i;
+ int bufsize;
+
+ // Allocate buffers for the components
+ for (i = 0; i < numcombs; i++) {
+ bufsize = int(scaletuning * combtuning[i]);
+ combL[i].setbuffer(new float[bufsize], bufsize);
+ bufsize += int(scaletuning * stereospread);
+ combR[i].setbuffer(new float[bufsize], bufsize);
+ }
+ for (i = 0; i < numallpasses; i++) {
+ bufsize = int(scaletuning * allpasstuning[i]);
+ allpassL[i].setbuffer(new float[bufsize], bufsize);
+ allpassL[i].setfeedback(0.5f);
+ bufsize += int(scaletuning * stereospread);
+ allpassR[i].setbuffer(new float[bufsize], bufsize);
+ allpassR[i].setfeedback(0.5f);
+ }
// Set default values
- allpassL[0].setfeedback(0.5f);
- allpassR[0].setfeedback(0.5f);
- allpassL[1].setfeedback(0.5f);
- allpassR[1].setfeedback(0.5f);
- allpassL[2].setfeedback(0.5f);
- allpassR[2].setfeedback(0.5f);
- allpassL[3].setfeedback(0.5f);
- allpassR[3].setfeedback(0.5f);
- setmode(initialmode);
- setwet(initialwet);
- setroomsize(initialroom);
- setdry(initialdry);
- setdamp(initialdamp);
- setwidth(initialwidth);
+ dry = initialdry;
+ wet = initialwet*scalewet;
+ damp = initialdamp*scaledamp;
+ roomsize = (initialroom*scaleroom) + offsetroom;
+ width = initialwidth;
+ mode = initialmode;
+ update();
// Buffer will be full of rubbish - so we MUST mute them
mute();
}
-void revmodel::mute() {
+revmodel::~revmodel()
+{
+ int i;
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].deletebuffer();
+ combR[i].deletebuffer();
+ }
+ for (i = 0; i < numallpasses; i++) {
+ allpassL[i].deletebuffer();
+ allpassR[i].deletebuffer();
+ }
+}
+
+void revmodel::mute()
+{
int i;
if (getmode() >= freezemode)
return;
- for (i = 0; i < numcombs; i++) {
+ for (i=0;i<numcombs;i++)
+ {
combL[i].mute();
combR[i].mute();
}
-
- for (i = 0; i < numallpasses; i++) {
+ for (i=0;i<numallpasses;i++)
+ {
allpassL[i].mute();
allpassR[i].mute();
}
-}
-
-void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
- float outL, outR, input;
-
- while (numsamples-- > 0) {
- int i;
-
- outL = outR = 0;
- input = (*inputL + *inputR) * gain;
-
- // Accumulate comb filters in parallel
- for (i = 0; i < numcombs; i++) {
- outL += combL[i].process(input);
- outR += combR[i].process(input);
- }
- // Feed through allpasses in series
- for (i = 0; i < numallpasses; i++) {
- outL = allpassL[i].process(outL);
- outR = allpassR[i].process(outR);
- }
-
- // Calculate output REPLACING anything already there
- *outputL = outL * wet1 + outR * wet2 + *inputL * dry;
- *outputR = outR * wet1 + outL * wet2 + *inputR * dry;
-
- // Increment sample pointers, allowing for interleave (if any)
- inputL += skip;
- inputR += skip;
- outputL += skip;
- outputR += skip;
- }
+ // Init LPF history
+ filtprev1 = 0;
+ filtprev2 = 0;
}
-void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
- float outL, outR, input;
+void revmodel::process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples)
+{
+ float outL,outR,input;
- while (numsamples-- > 0) {
+ while (numsamples-- > 0)
+ {
int i;
outL = outR = 0;
input = (*inputL + *inputR) * gain;
+ // Implementation of 2-stage IIR single-pole low-pass filter
+ // found at the entrance of reverb processing on real devices
+ filtprev1 += (input - filtprev1) * filtval;
+ filtprev2 += (filtprev1 - filtprev2) * filtval;
+ input = filtprev2;
+
+ int s = -1;
// Accumulate comb filters in parallel
- for (i = 0; i < numcombs; i++) {
- outL += combL[i].process(input);
- outR += combR[i].process(input);
+ for (i=0; i<numcombs; i++)
+ {
+ outL += s * combL[i].process(input);
+ outR += s * combR[i].process(input);
+ s = -s;
}
// Feed through allpasses in series
- for (i = 0; i < numallpasses; i++) {
+ for (i=0; i<numallpasses; i++)
+ {
outL = allpassL[i].process(outL);
outR = allpassR[i].process(outR);
}
- // Calculate output MIXING with anything already there
- *outputL += outL * wet1 + outR * wet2 + *inputL * dry;
- *outputR += outR * wet1 + outL * wet2 + *inputR * dry;
-
- // Increment sample pointers, allowing for interleave (if any)
- inputL += skip;
- inputR += skip;
- outputL += skip;
- outputR += skip;
+ // Calculate output REPLACING anything already there
+ *outputL = outL*wet1 + outR*wet2;
+ *outputR = outR*wet1 + outL*wet2;
+
+ inputL++;
+ inputR++;
+ outputL++;
+ outputR++;
}
}
-void revmodel::update() {
- // Recalculate internal values after parameter change
+void revmodel::update()
+{
+// Recalculate internal values after parameter change
int i;
- wet1 = wet * (width / 2 + 0.5f);
- wet2 = wet * ((1 - width) / 2);
+ wet1 = wet*(width/2 + 0.5f);
+ wet2 = wet*((1-width)/2);
- if (mode >= freezemode) {
+ if (mode >= freezemode)
+ {
roomsize1 = 1;
damp1 = 0;
gain = muted;
- } else {
+ }
+ else
+ {
roomsize1 = roomsize;
damp1 = damp;
gain = fixedgain;
}
- for (i = 0; i < numcombs; i++) {
+ for (i=0; i<numcombs; i++)
+ {
combL[i].setfeedback(roomsize1);
combR[i].setfeedback(roomsize1);
}
- for (i = 0; i < numcombs; i++) {
+ for (i=0; i<numcombs; i++)
+ {
combL[i].setdamp(damp1);
combR[i].setdamp(damp1);
}
@@ -250,58 +250,75 @@ void revmodel::update() {
// because as you develop the reverb model, you may
// wish to take dynamic action when they are called.
-void revmodel::setroomsize(float value) {
- roomsize = (value * scaleroom) + offsetroom;
+void revmodel::setroomsize(float value)
+{
+ roomsize = (value*scaleroom) + offsetroom;
update();
}
-float revmodel::getroomsize() {
- return (roomsize - offsetroom) / scaleroom;
+float revmodel::getroomsize()
+{
+ return (roomsize-offsetroom)/scaleroom;
}
-void revmodel::setdamp(float value) {
- damp = value * scaledamp;
+void revmodel::setdamp(float value)
+{
+ damp = value*scaledamp;
update();
}
-float revmodel::getdamp() {
- return damp / scaledamp;
+float revmodel::getdamp()
+{
+ return damp/scaledamp;
}
-void revmodel::setwet(float value) {
- wet = value * scalewet;
+void revmodel::setwet(float value)
+{
+ wet = value*scalewet;
update();
}
-float revmodel::getwet() {
- return wet / scalewet;
+float revmodel::getwet()
+{
+ return wet/scalewet;
}
-void revmodel::setdry(float value) {
- dry = value * scaledry;
+void revmodel::setdry(float value)
+{
+ dry = value*scaledry;
}
-float revmodel::getdry() {
- return dry / scaledry;
+float revmodel::getdry()
+{
+ return dry/scaledry;
}
-void revmodel::setwidth(float value) {
+void revmodel::setwidth(float value)
+{
width = value;
update();
}
-float revmodel::getwidth() {
+float revmodel::getwidth()
+{
return width;
}
-void revmodel::setmode(float value) {
+void revmodel::setmode(float value)
+{
mode = value;
update();
}
-float revmodel::getmode() {
+float revmodel::getmode()
+{
if (mode >= freezemode)
return 1;
else
return 0;
}
+
+void revmodel::setfiltval(float value)
+{
+ filtval = value;
+}
diff --git a/audio/softsynth/mt32/freeverb.h b/audio/softsynth/mt32/freeverb.h
index 39ae463970..ae4d48169e 100644
--- a/audio/softsynth/mt32/freeverb.h
+++ b/audio/softsynth/mt32/freeverb.h
@@ -1,24 +1,32 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
+#ifndef _freeverb_
+#define _freeverb_
+
+// Reverb model tuning values
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+const int numcombs = 8;
+const int numallpasses = 4;
+const float muted = 0;
+const float fixedgain = 0.015f;
+const float scalewet = 3;
+const float scaledry = 2;
+const float scaledamp = 0.4f;
+const float scaleroom = 0.28f;
+const float offsetroom = 0.7f;
+const float initialroom = 0.5f;
+const float initialdamp = 0.5f;
+const float initialwet = 1/scalewet;
+const float initialdry = 0;
+const float initialwidth = 1;
+const float initialmode = 0;
+const float freezemode = 0.5f;
+const int stereospread = 23;
+
+const int combtuning[] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617};
+const int allpasstuning[] = {556, 441, 341, 225};
// Macro for killing denormalled numbers
//
@@ -27,215 +35,155 @@
// Based on IS_DENORMAL macro by Jon Watte
// This code is public domain
-#ifndef FREEVERB_H
-#define FREEVERB_H
-
-// FIXME: Fix this really ugly hack
-inline float undenormalise(void *sample) {
- if (((*(unsigned int*)sample) & 0x7f800000) == 0)
+static inline float undenormalise(float x) {
+ union {
+ float f;
+ unsigned int i;
+ } u;
+ u.f = x;
+ if ((u.i & 0x7f800000) == 0) {
return 0.0f;
- return *(float*)sample;
+ }
+ return x;
}
-// Comb filter class declaration
+// Allpass filter declaration
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
-class comb {
+class allpass
+{
public:
- comb();
- void setbuffer(float *buf, int size);
- inline float process(float inp);
- void mute();
- void setdamp(float val);
- float getdamp();
- void setfeedback(float val);
- float getfeedback();
-private:
- float feedback;
- float filterstore;
- float damp1;
- float damp2;
- float *buffer;
- int bufsize;
- int bufidx;
+ allpass();
+ void setbuffer(float *buf, int size);
+ void deletebuffer();
+ inline float process(float inp);
+ void mute();
+ void setfeedback(float val);
+ float getfeedback();
+// private:
+ float feedback;
+ float *buffer;
+ int bufsize;
+ int bufidx;
};
// Big to inline - but crucial for speed
-inline float comb::process(float input) {
+inline float allpass::process(float input)
+{
float output;
+ float bufout;
- output = buffer[bufidx];
- undenormalise(&output);
-
- filterstore = (output * damp2) + (filterstore * damp1);
- undenormalise(&filterstore);
+ bufout = undenormalise(buffer[bufidx]);
- buffer[bufidx] = input + (filterstore * feedback);
+ output = -input + bufout;
+ buffer[bufidx] = input + (bufout*feedback);
- if (++bufidx >= bufsize)
- bufidx = 0;
+ if (++bufidx>=bufsize) bufidx = 0;
return output;
}
-// Allpass filter declaration
+// Comb filter class declaration
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
-class allpass {
+class comb
+{
public:
- allpass();
- void setbuffer(float *buf, int size);
- inline float process(float inp);
- void mute();
- void setfeedback(float val);
- float getfeedback();
+ comb();
+ void setbuffer(float *buf, int size);
+ void deletebuffer();
+ inline float process(float inp);
+ void mute();
+ void setdamp(float val);
+ float getdamp();
+ void setfeedback(float val);
+ float getfeedback();
private:
- float feedback;
- float *buffer;
- int bufsize;
- int bufidx;
+ float feedback;
+ float filterstore;
+ float damp1;
+ float damp2;
+ float *buffer;
+ int bufsize;
+ int bufidx;
};
// Big to inline - but crucial for speed
-inline float allpass::process(float input) {
+inline float comb::process(float input)
+{
float output;
- float bufout;
- bufout = buffer[bufidx];
- undenormalise(&bufout);
+ output = undenormalise(buffer[bufidx]);
- output = -input + bufout;
- buffer[bufidx] = input + (bufout * feedback);
+ filterstore = undenormalise((output*damp2) + (filterstore*damp1));
+
+ buffer[bufidx] = input + (filterstore*feedback);
- if (++bufidx >= bufsize)
- bufidx = 0;
+ if (++bufidx>=bufsize) bufidx = 0;
return output;
}
-
-// Reverb model tuning values
-
-const int numcombs = 8;
-const int numallpasses = 4;
-const float muted = 0;
-const float fixedgain = 0.015f;
-const float scalewet = 3;
-const float scaledry = 2;
-const float scaledamp = 0.4f;
-const float scaleroom = 0.28f;
-const float offsetroom = 0.7f;
-const float initialroom = 0.5f;
-const float initialdamp = 0.5f;
-const float initialwet = 1 / scalewet;
-const float initialdry = 0;
-const float initialwidth = 1;
-const float initialmode = 0;
-const float freezemode = 0.5f;
-const int stereospread = 23;
-
-// These values assume 44.1KHz sample rate
-// they will probably be OK for 48KHz sample rate
-// but would need scaling for 96KHz (or other) sample rates.
-// The values were obtained by listening tests.
-const int combtuningL1 = 1116;
-const int combtuningR1 = 1116 + stereospread;
-const int combtuningL2 = 1188;
-const int combtuningR2 = 1188 + stereospread;
-const int combtuningL3 = 1277;
-const int combtuningR3 = 1277 + stereospread;
-const int combtuningL4 = 1356;
-const int combtuningR4 = 1356 + stereospread;
-const int combtuningL5 = 1422;
-const int combtuningR5 = 1422 + stereospread;
-const int combtuningL6 = 1491;
-const int combtuningR6 = 1491 + stereospread;
-const int combtuningL7 = 1557;
-const int combtuningR7 = 1557 + stereospread;
-const int combtuningL8 = 1617;
-const int combtuningR8 = 1617 + stereospread;
-const int allpasstuningL1 = 556;
-const int allpasstuningR1 = 556 + stereospread;
-const int allpasstuningL2 = 441;
-const int allpasstuningR2 = 441 + stereospread;
-const int allpasstuningL3 = 341;
-const int allpasstuningR3 = 341 + stereospread;
-const int allpasstuningL4 = 225;
-const int allpasstuningR4 = 225 + stereospread;
-
-
// Reverb model declaration
+//
+// Written by Jezar at Dreampoint, June 2000
+// Modifications by Jerome Fisher, 2009
+// http://www.dreampoint.co.uk
+// This code is public domain
-class revmodel {
+class revmodel
+{
public:
- revmodel();
- void mute();
- void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
- void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
- void setroomsize(float value);
- float getroomsize();
- void setdamp(float value);
- float getdamp();
- void setwet(float value);
- float getwet();
- void setdry(float value);
- float getdry();
- void setwidth(float value);
- float getwidth();
- void setmode(float value);
- float getmode();
+ revmodel(float scaletuning);
+ ~revmodel();
+ void mute();
+ void process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples);
+ void setroomsize(float value);
+ float getroomsize();
+ void setdamp(float value);
+ float getdamp();
+ void setwet(float value);
+ float getwet();
+ void setdry(float value);
+ float getdry();
+ void setwidth(float value);
+ float getwidth();
+ void setmode(float value);
+ float getmode();
+ void setfiltval(float value);
private:
- void update();
-
- float gain;
- float roomsize, roomsize1;
- float damp, damp1;
- float wet, wet1, wet2;
- float dry;
- float width;
- float mode;
-
- // The following are all declared inline
- // to remove the need for dynamic allocation
- // with its subsequent error-checking messiness
+ void update();
+private:
+ float gain;
+ float roomsize,roomsize1;
+ float damp,damp1;
+ float wet,wet1,wet2;
+ float dry;
+ float width;
+ float mode;
+
+ // LPF stuff
+ float filtval;
+ float filtprev1;
+ float filtprev2;
// Comb filters
- comb combL[numcombs];
- comb combR[numcombs];
+ comb combL[numcombs];
+ comb combR[numcombs];
// Allpass filters
allpass allpassL[numallpasses];
allpass allpassR[numallpasses];
-
- // Buffers for the combs
- float bufcombL1[combtuningL1];
- float bufcombR1[combtuningR1];
- float bufcombL2[combtuningL2];
- float bufcombR2[combtuningR2];
- float bufcombL3[combtuningL3];
- float bufcombR3[combtuningR3];
- float bufcombL4[combtuningL4];
- float bufcombR4[combtuningR4];
- float bufcombL5[combtuningL5];
- float bufcombR5[combtuningR5];
- float bufcombL6[combtuningL6];
- float bufcombR6[combtuningR6];
- float bufcombL7[combtuningL7];
- float bufcombR7[combtuningR7];
- float bufcombL8[combtuningL8];
- float bufcombR8[combtuningR8];
-
- // Buffers for the allpasses
- float bufallpassL1[allpasstuningL1];
- float bufallpassR1[allpasstuningR1];
- float bufallpassL2[allpasstuningL2];
- float bufallpassR2[allpasstuningR2];
- float bufallpassL3[allpasstuningL3];
- float bufallpassR3[allpasstuningR3];
- float bufallpassL4[allpasstuningL4];
- float bufallpassR4[allpasstuningR4];
};
-#endif
+#endif//_freeverb_
diff --git a/audio/softsynth/mt32/i386.cpp b/audio/softsynth/mt32/i386.cpp
deleted file mode 100644
index f092189d76..0000000000
--- a/audio/softsynth/mt32/i386.cpp
+++ /dev/null
@@ -1,849 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include "mt32emu.h"
-
-#ifdef MT32EMU_HAVE_X86
-
-namespace MT32Emu {
-
-#ifndef _MSC_VER
-
-#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value))
-#define cpuid_flag (1 << 21)
-
-static inline bool atti386_DetectCPUID() {
- unsigned int result;
-
- // Is there a cpuid?
- result = cpuid_flag; // set test
- eflag(result);
- if (!(result & cpuid_flag))
- return false;
-
- result = 0; // clear test
- eflag(result);
- if (result & cpuid_flag)
- return false;
-
- return true;
-}
-
-static inline bool atti386_DetectSIMD() {
- unsigned int result;
-
- if (atti386_DetectCPUID() == false)
- return false;
-
- /* check cpuid */
- __asm__ __volatile__(
- "pushl %%ebx \n" \
- "movl $1, %%eax \n" \
- "cpuid \n" \
- "movl %%edx, %0 \n" \
- "popl %%ebx \n" \
- : "=r"(result) : : "eax", "ecx", "edx");
-
- if (result & (1 << 25))
- return true;
-
- return false;
-}
-
-static inline bool atti386_Detect3DNow() {
- unsigned int result;
-
- if (atti386_DetectCPUID() == false)
- return false;
-
- // get cpuid
- __asm__ __volatile__(
- "pushl %%ebx \n" \
- "movl $0x80000001, %%eax \n" \
- "cpuid \n" \
- "movl %%edx, %0 \n" \
- "popl %%ebx \n" \
- : "=r"(result) : : "eax", "ecx", "edx");
-
- if (result & 0x80000000)
- return true;
-
- return false;
-}
-
-
-static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr) {
- __asm__ __volatile__ (
- "pushl %1 \n" \
- "pushl %2 \n" \
- "movss 0(%0), %%xmm1 \n" \
- "movups 0(%1), %%xmm2 \n" \
- "movlps 0(%2), %%xmm3 \n" \
- " \n" \
- "shufps $0x44, %%xmm3, %%xmm3 \n" \
- " \n" \
- "mulps %%xmm3, %%xmm2 \n" \
- " \n" \
- "subss %%xmm2, %%xmm1 \n" \
- "shufps $0x39, %%xmm2, %%xmm2 \n" \
- "subss %%xmm2, %%xmm1 \n" \
- " \n" \
- "movss %%xmm1, 0(%2) \n" \
- " \n" \
- "shufps $0x39, %%xmm2, %%xmm2 \n" \
- "addss %%xmm2, %%xmm1 \n" \
- " \n" \
- "shufps $0x39, %%xmm2, %%xmm2 \n" \
- "addss %%xmm2, %%xmm1 \n" \
- " \n" \
- "movss %%xmm3, 4(%2) \n" \
- " \n" \
- "addl $16, %1 \n" \
- "addl $8, %2 \n" \
- " \n" \
- "movups 0(%1), %%xmm2 \n" \
- " \n" \
- "movlps 0(%2), %%xmm3 \n" \
- "shufps $0x44, %%xmm3, %%xmm3 \n" \
- " \n" \
- "mulps %%xmm3, %%xmm2 \n" \
- " \n" \
- "subss %%xmm2, %%xmm1 \n" \
- "shufps $0x39, %%xmm2, %%xmm2 \n" \
- "subss %%xmm2, %%xmm1 \n" \
- " \n" \
- "movss %%xmm1, 0(%2) \n" \
- " \n" \
- "shufps $0x39, %%xmm2, %%xmm2 \n" \
- "addss %%xmm2, %%xmm1 \n" \
- " \n" \
- "shufps $0x39, %%xmm2, %%xmm2 \n" \
- "addss %%xmm2, %%xmm1 \n" \
- " \n" \
- "movss %%xmm3, 4(%2) \n" \
- "movss %%xmm1, 0(%0) \n" \
- "popl %2 \n" \
- "popl %1 \n" \
- : : "r"(output), "r"(coef_ptr), "r"(hist1_ptr)
- : "memory"
-#ifdef __SSE__
- , "xmm1", "xmm2", "xmm3"
-#endif
- );
-
- return *output;
-}
-
-static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr) {
- float tmp;
-
- __asm__ __volatile__ (
- "movq %0, %%mm1 \n" \
- " \n" \
- "movl %1, %%edi \n" \
- "movq 0(%%edi), %%mm2 \n" \
- " \n" \
- "movl %2, %%eax; \n" \
- "movq 0(%%eax), %%mm3 \n" \
- " \n" \
- "pfmul %%mm3, %%mm2 \n" \
- "pfsub %%mm2, %%mm1 \n" \
- " \n" \
- "psrlq $32, %%mm2 \n" \
- "pfsub %%mm2, %%mm1 \n" \
- " \n" \
- "movd %%mm1, %3 \n" \
- " \n" \
- "addl $8, %%edi \n" \
- "movq 0(%%edi), %%mm2 \n" \
- "movq 0(%%eax), %%mm3 \n" \
- " \n" \
- "pfmul %%mm3, %%mm2 \n" \
- "pfadd %%mm2, %%mm1 \n" \
- " \n" \
- "psrlq $32, %%mm2 \n" \
- "pfadd %%mm2, %%mm1 \n" \
- " \n" \
- "pushl %3 \n" \
- "popl 0(%%eax) \n" \
- " \n" \
- "movd %%mm3, 4(%%eax) \n" \
- " \n" \
- "addl $8, %%edi \n" \
- "addl $8, %%eax \n" \
- " \n" \
- "movq 0(%%edi), %%mm2 \n" \
- "movq 0(%%eax), %%mm3 \n" \
- " \n" \
- "pfmul %%mm3, %%mm2 \n" \
- "pfsub %%mm2, %%mm1 \n" \
- " \n" \
- "psrlq $32, %%mm2 \n" \
- "pfsub %%mm2, %%mm1 \n" \
- " \n" \
- "movd %%mm1, %3 \n" \
- " \n" \
- "addl $8, %%edi \n" \
- "movq 0(%%edi), %%mm2 \n" \
- "movq 0(%%eax), %%mm3 \n" \
- " \n" \
- "pfmul %%mm3, %%mm2 \n" \
- "pfadd %%mm2, %%mm1 \n" \
- " \n" \
- "psrlq $32, %%mm2 \n" \
- "pfadd %%mm2, %%mm1 \n" \
- " \n" \
- "pushl %3 \n" \
- "popl 0(%%eax) \n" \
- "movd %%mm3, 4(%%eax) \n" \
- " \n" \
- "movd %%mm1, %0 \n" \
- "femms \n" \
- : "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp)
- : "eax", "edi", "memory"
-#ifdef __MMX__
- , "mm1", "mm2", "mm3"
-#endif
- );
-
- return output;
-}
-
-static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd) {
- __asm__ __volatile__(
- "movl %0, %%ecx \n" \
- "movw %1, %%ax \n" \
- "shll $16, %%eax \n" \
- "movw %1, %%ax \n" \
- "movd %%eax, %%mm3 \n" \
- "movd %%eax, %%mm2 \n" \
- "psllq $32, %%mm3 \n" \
- "por %%mm2, %%mm3 \n" \
- "movl %2, %%esi \n" \
- "movl %3, %%edi \n" \
- "1: \n" \
- "movq 0(%%esi), %%mm1 \n" \
- "movq 0(%%edi), %%mm2 \n" \
- "pmulhw %%mm3, %%mm1 \n" \
- "paddw %%mm2, %%mm1 \n" \
- "movq %%mm1, 0(%%edi) \n" \
- " \n" \
- "addl $8, %%esi \n" \
- "addl $8, %%edi \n" \
- " \n" \
- "decl %%ecx \n" \
- "cmpl $0, %%ecx \n" \
- "jg 1b \n" \
- "emms \n" \
- : : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd)
- : "eax", "ecx", "edi", "esi", "memory"
-#ifdef __MMX__
- , "mm1", "mm2", "mm3"
-#endif
- );
-}
-
-static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor) {
- __asm__ __volatile__(
- "movl %4, %%ecx \n" \
- "shrl $1, %%ecx \n" \
- "addl $4, %%ecx \n" \
- "pushl %%ecx \n" \
- " \n" \
- "movl %0, %%esi \n" \
- "movups 0(%%esi), %%xmm1 \n" \
- " \n" \
- "movl %1, %%esi \n" \
- "movl %2, %%edi \n" \
- "1: \n" \
- "xorl %%eax, %%eax \n" \
- "movw 0(%1), %%ax \n" \
- "cwde \n" \
- "incl %1 \n" \
- "incl %1 \n" \
- "movd %%eax, %%mm1 \n" \
- "psrlq $32, %%mm1 \n" \
- "movw 0(%1), %%ax \n" \
- "incl %1 \n" \
- "incl %1 \n" \
- "movd %%eax, %%mm2 \n" \
- "por %%mm2, %%mm1 \n" \
- " \n" \
- "decl %%ecx \n" \
- "jnz 1b \n" \
- " \n" \
- "popl %%ecx \n" \
- "movl %1, %%esi \n" \
- "movl %3, %%edi \n" \
- "incl %%esi \n" \
- "2: \n" \
- "decl %%ecx \n" \
- "jnz 2b \n" \
- : : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len)
- : "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory");
-}
-
-static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
- __asm__ __volatile__(
- "movl %0, %%ecx \n" \
- "movl %1, %%esi \n" \
- "movl %2, %%edi \n" \
- "1: \n" \
- "movq 0(%%edi), %%mm1 \n" \
- "movq 0(%%esi), %%mm2 \n" \
- "paddw %%mm2, %%mm1 \n" \
- "movq %%mm1, 0(%%esi) \n" \
- "addl $8, %%edi \n" \
- "addl $8, %%esi \n" \
- "decl %%ecx \n" \
- "cmpl $0, %%ecx \n" \
- "jg 1b \n" \
- "emms \n" \
- : : "g"(len), "g"(buf1), "g"(buf2)
- : "ecx", "edi", "esi", "memory"
-#ifdef __MMX__
- , "mm1", "mm2"
-#endif
- );
-}
-
-static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
- __asm__ __volatile__(
- "movl %0, %%ecx \n" \
- "movl %1, %%esi \n" \
- "movl %2, %%edi \n" \
- "1: \n" \
- "movq 0(%%esi), %%mm1 \n" \
- "movq 0(%%edi), %%mm2 \n" \
- "movq %%mm1, %%mm3 \n" \
- "pmulhw %%mm2, %%mm1 \n" \
- "paddw %%mm3, %%mm1 \n" \
- "movq %%mm1, 0(%%esi) \n" \
- "addl $8, %%edi \n" \
- "addl $8, %%esi \n" \
- "decl %%ecx \n" \
- "cmpl $0, %%ecx \n" \
- "jg 1b \n" \
- "emms \n" \
- : : "g"(len), "g"(buf1), "g"(buf2)
- : "ecx", "edi", "esi", "memory"
-#ifdef __MMX__
- , "mm1", "mm2", "mm3"
-#endif
- );
-}
-
-static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
- __asm__ __volatile__(
- "movl %0, %%ecx \n" \
- "movl %1, %%esi \n" \
- "movl %2, %%edi \n" \
- "1: \n" \
- "movq 0(%%esi), %%mm1 \n" \
- "movq 0(%%edi), %%mm2 \n" \
- "pmulhw %%mm2, %%mm1 \n" \
- "movq %%mm1, 0(%%esi) \n" \
- "addl $8, %%edi \n" \
- "addl $8, %%esi \n" \
- "decl %%ecx \n" \
- "cmpl $0, %%ecx \n" \
- "jg 1b \n" \
- "emms \n" \
- : : "g"(len), "g"(buf1), "g"(buf2)
- : "ecx", "edi", "esi", "memory"
-#ifdef __MMX__
- , "mm1", "mm2"
-#endif
- );
-}
-
-static inline void atti386_partialProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *p1buf) {
- __asm__ __volatile__(
- "movl %0, %%ecx \n" \
- "movw %1, %%ax \n" \
- "shll $16, %%eax \n" \
- "movw %2, %%ax \n" \
- "movd %%eax, %%mm1 \n" \
- "movd %%eax, %%mm2 \n" \
- "psllq $32, %%mm1 \n" \
- "por %%mm2, %%mm1 \n" \
- "movl %3, %%edi \n" \
- "movl %4, %%esi \n" \
- "pushl %%ebx \n" \
- "1: \n" \
- "movw 0(%%esi), %%bx \n" \
- "addl $2, %%esi \n" \
- "movw 0(%%esi), %%dx \n" \
- "addl $2, %%esi \n" \
- "" \
- "movw %%dx, %%ax \n" \
- "shll $16, %%eax \n" \
- "movw %%dx, %%ax \n" \
- "movd %%eax, %%mm2 \n" \
- "psllq $32, %%mm2 \n" \
- "movw %%bx, %%ax \n" \
- "shll $16, %%eax \n" \
- "movw %%bx, %%ax \n" \
- "movd %%eax, %%mm3 \n" \
- "por %%mm3, %%mm2 \n" \
- "" \
- "pmulhw %%mm1, %%mm2 \n" \
- "movq %%mm2, 0(%%edi) \n" \
- "addl $8, %%edi \n" \
- "" \
- "decl %%ecx \n" \
- "cmpl $0, %%ecx \n" \
- "jg 1b \n" \
- "emms \n" \
- "popl %%ebx \n" \
- : : "g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf)
- : "eax", "ecx", "edx", "edi", "esi", "memory"
-#ifdef __MMX__
- , "mm1", "mm2", "mm3"
-#endif
- );
-}
-
-#endif
-
-bool DetectSIMD() {
-#ifdef _MSC_VER
- bool found_simd;
- __asm {
- pushfd
- pop eax // get EFLAGS into eax
- mov ebx,eax // keep a copy
- xor eax,0x200000
- // toggle CPUID bit
-
- push eax
- popfd // set new EFLAGS
- pushfd
- pop eax // EFLAGS back into eax
-
- xor eax,ebx
- // have we changed the ID bit?
-
- je NO_SIMD
- // No, no CPUID instruction
-
- // we could toggle the
- // ID bit so CPUID is present
- mov eax,1
-
- cpuid // get processor features
- test edx,1<<25 // check the SIMD bit
- jz NO_SIMD
- mov found_simd,1
- jmp DONE
- NO_SIMD:
- mov found_simd,0
- DONE:
- }
- return found_simd;
-#else
- return atti386_DetectSIMD();
-#endif
-}
-
-bool Detect3DNow() {
-#ifdef _MSC_VER
- bool found3D = false;
- __asm {
- pushfd
- pop eax
- mov edx, eax
- xor eax, 00200000h
- push eax
- popfd
- pushfd
- pop eax
- xor eax, edx
- jz NO_3DNOW
-
- mov eax, 80000000h
- cpuid
-
- cmp eax, 80000000h
- jbe NO_3DNOW
-
- mov eax, 80000001h
- cpuid
- test edx, 80000000h
- jz NO_3DNOW
- mov found3D, 1
-NO_3DNOW:
-
- }
- return found3D;
-#else
- return atti386_Detect3DNow();
-#endif
-}
-
-float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr) {
- float output;
-
- // 1st number of coefficients array is overall input scale factor, or filter gain
- output = input * (*coef_ptr++);
-
-#ifdef _MSC_VER
- __asm {
-
- movss xmm1, output
-
- mov eax, coef_ptr
- movups xmm2, [eax]
-
- mov eax, hist1_ptr
- movlps xmm3, [eax]
- shufps xmm3, xmm3, 44h
- // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr
-
- mulps xmm2, xmm3
-
- subss xmm1, xmm2
- // Rotate elements right
- shufps xmm2, xmm2, 39h
- subss xmm1, xmm2
-
- // Store new_hist
- movss DWORD PTR [eax], xmm1
-
- // Rotate elements right
- shufps xmm2, xmm2, 39h
- addss xmm1, xmm2
-
- // Rotate elements right
- shufps xmm2, xmm2, 39h
- addss xmm1, xmm2
-
- // Store previous hist
- movss DWORD PTR [eax+4], xmm3
-
- add coef_ptr, 16
- add hist1_ptr, 8
-
- mov eax, coef_ptr
- movups xmm2, [eax]
-
- mov eax, hist1_ptr
- movlps xmm3, [eax]
- shufps xmm3, xmm3, 44h
- // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr
-
- mulps xmm2, xmm3
-
- subss xmm1, xmm2
- // Rotate elements right
- shufps xmm2, xmm2, 39h
- subss xmm1, xmm2
-
- // Store new_hist
- movss DWORD PTR [eax], xmm1
-
- // Rotate elements right
- shufps xmm2, xmm2, 39h
- addss xmm1, xmm2
-
- // Rotate elements right
- shufps xmm2, xmm2, 39h
- addss xmm1, xmm2
-
- // Store previous hist
- movss DWORD PTR [eax+4], xmm3
-
- movss output, xmm1
- }
-#else
- output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr);
-#endif
- return output;
-}
-
-float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr) {
- float output;
-
- // 1st number of coefficients array is overall input scale factor, or filter gain
- output = input * (*coef_ptr++);
-
- // I find it very sad that 3DNow requires twice as many instructions as Intel's SSE
- // Intel does have the upper hand here.
-#ifdef _MSC_VER
- float tmp;
- __asm {
- movq mm1, output
- mov ebx, coef_ptr
- movq mm2, [ebx]
-
- mov eax, hist1_ptr;
- movq mm3, [eax]
-
- pfmul mm2, mm3
- pfsub mm1, mm2
-
- psrlq mm2, 32
- pfsub mm1, mm2
-
- // Store new hist
- movd tmp, mm1
-
- add ebx, 8
- movq mm2, [ebx]
- movq mm3, [eax]
-
- pfmul mm2, mm3
- pfadd mm1, mm2
-
- psrlq mm2, 32
- pfadd mm1, mm2
-
- push tmp
- pop DWORD PTR [eax]
-
- movd DWORD PTR [eax+4], mm3
-
- add ebx, 8
- add eax, 8
-
- movq mm2, [ebx]
- movq mm3, [eax]
-
- pfmul mm2, mm3
- pfsub mm1, mm2
-
- psrlq mm2, 32
- pfsub mm1, mm2
-
- // Store new hist
- movd tmp, mm1
-
- add ebx, 8
- movq mm2, [ebx]
- movq mm3, [eax]
-
- pfmul mm2, mm3
- pfadd mm1, mm2
-
- psrlq mm2, 32
- pfadd mm1, mm2
-
- push tmp
- pop DWORD PTR [eax]
- movd DWORD PTR [eax+4], mm3
-
- movd output, mm1
-
- femms
- }
-#else
- output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr);
-#endif
- return output;
-}
-
-#if MT32EMU_USE_MMX > 0
-
-int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf) {
- int tmplen = len >> 1;
- if (tmplen == 0) {
- return 0;
- }
-#ifdef _MSC_VER
- __asm {
- mov ecx,tmplen
- mov ax, leftvol
- shl eax,16
- mov ax, rightvol
- movd mm1, eax
- movd mm2, eax
- psllq mm1, 32
- por mm1, mm2
- mov edi, partialBuf
- mov esi, mixedBuf
-mmxloop1:
- mov bx, [esi]
- add esi,2
- mov dx, [esi]
- add esi,2
-
- mov ax, dx
- shl eax, 16
- mov ax, dx
- movd mm2,eax
- psllq mm2, 32
- mov ax, bx
- shl eax, 16
- mov ax, bx
- movd mm3,eax
- por mm2,mm3
-
- pmulhw mm2, mm1
- movq [edi], mm2
- add edi, 8
-
- dec ecx
- cmp ecx,0
- jg mmxloop1
- emms
- }
-#else
- atti386_partialProductOutput(tmplen, leftvol, rightvol, partialBuf, mixedBuf);
-#endif
- return tmplen << 1;
-}
-
-int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
- int tmplen = len >> 2;
- if (tmplen == 0) {
- return 0;
- }
-#ifdef _MSC_VER
- __asm {
- mov ecx, tmplen
- mov esi, buf1
- mov edi, buf2
-
-mixloop1:
- movq mm1, [edi]
- movq mm2, [esi]
- paddw mm1,mm2
- movq [esi],mm1
- add edi,8
- add esi,8
-
- dec ecx
- cmp ecx,0
- jg mixloop1
- emms
- }
-#else
- atti386_mixBuffers(buf1, buf2, tmplen);
-#endif
- return tmplen << 2;
-}
-
-
-int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
- int tmplen = len >> 2;
- if (tmplen == 0) {
- return 0;
- }
-#ifdef _MSC_VER
- __asm {
- mov ecx, tmplen
- mov esi, buf1
- mov edi, buf2
-
-mixloop2:
- movq mm1, [esi]
- movq mm2, [edi]
- movq mm3, mm1
- pmulhw mm1, mm2
- paddw mm1,mm3
- movq [esi],mm1
- add edi,8
- add esi,8
-
- dec ecx
- cmp ecx,0
- jg mixloop2
- emms
- }
-#else
- atti386_mixBuffersRingMix(buf1, buf2, tmplen);
-#endif
- return tmplen << 2;
-}
-
-int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
- int tmplen = len >> 2;
- if (tmplen == 0) {
- return 0;
- }
-#ifdef _MSC_VER
- __asm {
- mov ecx, tmplen
- mov esi, buf1
- mov edi, buf2
-
-mixloop3:
- movq mm1, [esi]
- movq mm2, [edi]
- pmulhw mm1, mm2
- movq [esi],mm1
- add edi,8
- add esi,8
-
- dec ecx
- cmp ecx,0
- jg mixloop3
- emms
- }
-#else
- atti386_mixBuffersRing(buf1, buf2, tmplen);
-#endif
- return tmplen << 2;
-}
-
-int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
- int tmplen = (len >> 1);
- if (tmplen == 0) {
- return 0;
- }
-#ifdef _MSC_VER
- __asm {
- mov ecx, tmplen
- mov ax,volume
- shl eax,16
- mov ax,volume
- movd mm3,eax
- movd mm2,eax
- psllq mm3, 32
- por mm3,mm2
- mov esi, useBuf
- mov edi, stream
-mixloop4:
- movq mm1, [esi]
- movq mm2, [edi]
- pmulhw mm1, mm3
- paddw mm1,mm2
- movq [edi], mm1
-
- add esi,8
- add edi,8
-
- dec ecx
- cmp ecx,0
- jg mixloop4
- emms
- }
-#else
- atti386_produceOutput1(tmplen, volume, useBuf, stream);
-#endif
- return tmplen << 1;
-}
-
-#endif
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/i386.h b/audio/softsynth/mt32/i386.h
deleted file mode 100644
index e8644411cd..0000000000
--- a/audio/softsynth/mt32/i386.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef MT32EMU_I386_H
-#define MT32EMU_I386_H
-
-namespace MT32Emu {
-#ifdef MT32EMU_HAVE_X86
-
-// Function that detects the availablity of SSE SIMD instructions
-bool DetectSIMD();
-// Function that detects the availablity of 3DNow instructions
-bool Detect3DNow();
-
-float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr);
-float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr);
-float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr);
-
-#if MT32EMU_USE_MMX > 0
-int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf);
-int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len);
-int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len);
-int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len);
-int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume);
-#endif
-
-#endif
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h
new file mode 100644
index 0000000000..60f492f9f0
--- /dev/null
+++ b/audio/softsynth/mt32/mmath.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_MMATH_H
+#define MT32EMU_MMATH_H
+
+#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y)))
+#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y)))
+#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point)
+#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point))
+
+#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))
+
+namespace MT32Emu {
+
+// Mathematical constants
+const double DOUBLE_PI = 3.141592653589793;
+const double DOUBLE_LN_10 = 2.302585092994046;
+const float FLOAT_PI = 3.1415927f;
+const float FLOAT_2PI = 6.2831853f;
+const float FLOAT_LN_2 = 0.6931472f;
+const float FLOAT_LN_10 = 2.3025851f;
+
+static inline float POWF(float x, float y) {
+ return pow(x, y);
+}
+
+static inline float EXPF(float x) {
+ return exp(x);
+}
+
+static inline float EXP2F(float x) {
+#ifdef __APPLE__
+ // on OSX exp2f() is 1.59 times faster than "exp() and the multiplication with FLOAT_LN_2"
+ return exp2f(x);
+#else
+ return exp(FLOAT_LN_2 * x);
+#endif
+}
+
+static inline float EXP10F(float x) {
+ return exp(FLOAT_LN_10 * x);
+}
+
+static inline float LOGF(float x) {
+ return log(x);
+}
+
+static inline float LOG2F(float x) {
+ return log(x) / FLOAT_LN_2;
+}
+
+static inline float LOG10F(float x) {
+ return log10(x);
+}
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk
index a8329bc98c..995e450076 100644
--- a/audio/softsynth/mt32/module.mk
+++ b/audio/softsynth/mt32/module.mk
@@ -1,13 +1,19 @@
MODULE := audio/softsynth/mt32
MODULE_OBJS := \
- mt32_file.o \
- i386.o \
- part.o \
- partial.o \
- partialManager.o \
- synth.o \
- tables.o \
+ AReverbModel.o \
+ DelayReverb.o \
+ FreeverbModel.o \
+ LA32Ramp.o \
+ Part.o \
+ Partial.o \
+ PartialManager.o \
+ Poly.o \
+ Synth.o \
+ TVA.o \
+ TVF.o \
+ TVP.o \
+ Tables.o \
freeverb.o
# Include common rules
diff --git a/audio/softsynth/mt32/mt32_file.cpp b/audio/softsynth/mt32/mt32_file.cpp
deleted file mode 100644
index 643082b086..0000000000
--- a/audio/softsynth/mt32/mt32_file.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-
-#include "mt32emu.h"
-
-namespace MT32Emu {
-
-bool File::readBit16u(Bit16u *in) {
- Bit8u b[2];
- if (read(&b[0], 2) != 2)
- return false;
- *in = ((b[0] << 8) | b[1]);
- return true;
-}
-
-bool File::readBit32u(Bit32u *in) {
- Bit8u b[4];
- if (read(&b[0], 4) != 4)
- return false;
- *in = ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
- return true;
-}
-
-bool File::writeBit16u(Bit16u out) {
- if (!writeBit8u((Bit8u)((out & 0xFF00) >> 8))) {
- return false;
- }
- if (!writeBit8u((Bit8u)(out & 0x00FF))) {
- return false;
- }
- return true;
-}
-
-bool File::writeBit32u(Bit32u out) {
- if (!writeBit8u((Bit8u)((out & 0xFF000000) >> 24))) {
- return false;
- }
- if (!writeBit8u((Bit8u)((out & 0x00FF0000) >> 16))) {
- return false;
- }
- if (!writeBit8u((Bit8u)((out & 0x0000FF00) >> 8))) {
- return false;
- }
- if (!writeBit8u((Bit8u)(out & 0x000000FF))) {
- return false;
- }
- return true;
-}
-
-} // End of namespace MT32Emu
diff --git a/audio/softsynth/mt32/mt32_file.h b/audio/softsynth/mt32/mt32_file.h
deleted file mode 100644
index e6641660ee..0000000000
--- a/audio/softsynth/mt32/mt32_file.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef MT32EMU_FILE_H
-#define MT32EMU_FILE_H
-
-#include "common/scummsys.h"
-
-namespace MT32Emu {
-
-class File {
-public:
- enum OpenMode {
- OpenMode_read = 0,
- OpenMode_write = 1
- };
- virtual ~File() {}
- virtual void close() = 0;
- virtual size_t read(void *in, size_t size) = 0;
- virtual bool readBit8u(Bit8u *in) = 0;
- virtual bool readBit16u(Bit16u *in);
- virtual bool readBit32u(Bit32u *in);
- virtual size_t write(const void *out, size_t size) = 0;
- virtual bool writeBit8u(Bit8u out) = 0;
- // Note: May write a single byte to the file before failing
- virtual bool writeBit16u(Bit16u out);
- // Note: May write some (<4) bytes to the file before failing
- virtual bool writeBit32u(Bit32u out);
- virtual bool isEOF() = 0;
-};
-
-} // End of namespace MT32Emu
-
-#endif
diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h
index 6eedf04bc0..a8685a30e6 100644
--- a/audio/softsynth/mt32/mt32emu.h
+++ b/audio/softsynth/mt32/mt32emu.h
@@ -1,37 +1,70 @@
-/* Copyright (c) 2003-2005 Various contributors
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
*
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_MT32EMU_H
#define MT32EMU_MT32EMU_H
// Debugging
-// Show the instruments played
-#define MT32EMU_MONITOR_INSTRUMENTS 1
-// Shows number of partials MT-32 is playing, and on which parts
+
+// 0: Standard debug output is not stamped with the rendered sample count
+// 1: Standard debug output is stamped with the rendered sample count
+// NOTE: The "samplestamp" corresponds to the end of the last completed rendering run.
+// This is important to bear in mind for debug output that occurs during a run.
+#define MT32EMU_DEBUG_SAMPLESTAMPS 0
+
+// 0: No debug output for initialisation progress
+// 1: Debug output for initialisation progress
+#define MT32EMU_MONITOR_INIT 0
+
+// 0: No debug output for MIDI events
+// 1: Debug output for weird MIDI events
+#define MT32EMU_MONITOR_MIDI 0
+
+// 0: No debug output for note on/off
+// 1: Basic debug output for note on/off
+// 2: Comprehensive debug output for note on/off
+#define MT32EMU_MONITOR_INSTRUMENTS 0
+
+// 0: No debug output for partial allocations
+// 1: Show partial stats when an allocation fails
+// 2: Show partial stats with every new poly
+// 3: Show individual partial allocations/deactivations
#define MT32EMU_MONITOR_PARTIALS 0
-// Determines how the waveform cache file is handled (must be regenerated after sampling rate change)
-#define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache
-//#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generate but don't save cache
-//#define MT32EMU_WAVECACHEMODE 2 // Ignore existing cache, generate and save cache
-//#define MT32EMU_WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache
+
+// 0: No debug output for sysex
+// 1: Basic debug output for sysex
+#define MT32EMU_MONITOR_SYSEX 0
+
+// 0: No debug output for sysex writes to the timbre areas
+// 1: Debug output with the name and location of newly-written timbres
+// 2: Complete dump of timbre parameters for newly-written timbres
+#define MT32EMU_MONITOR_TIMBRES 0
+
+// 0: No TVA/TVF-related debug output.
+// 1: Shows changes to TVA/TVF target, increment and phase.
+#define MT32EMU_MONITOR_TVA 0
+#define MT32EMU_MONITOR_TVF 0
+
+
+// 0: Use LUTs to speedup WG
+// 1: Use precise float math
+#define MT32EMU_ACCURATE_WG 1
+
+#define MT32EMU_USE_EXTINT 0
// Configuration
// The maximum number of partials playing simultaneously
@@ -39,32 +72,43 @@
// The maximum number of notes playing simultaneously per part.
// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial.
#define MT32EMU_MAX_POLY 32
-// This calculates the exact frequencies of notes as they are played, instead of offsetting from pre-cached semitones. Potentially very slow.
-#define MT32EMU_ACCURATENOTES 0
-
-#if (defined (_MSC_VER) && defined(_M_IX86))
-#define MT32EMU_HAVE_X86
-#elif defined(__GNUC__)
-#if __GNUC__ >= 3 && defined(__i386__)
-#define MT32EMU_HAVE_X86
-#endif
-#endif
-#ifdef MT32EMU_HAVE_X86
-#define MT32EMU_USE_MMX 1
-#else
-#define MT32EMU_USE_MMX 0
-#endif
+// If non-zero, deletes reverb buffers that are not in use to save memory.
+// If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.
+#define MT32EMU_REDUCE_REVERB_MEMORY 1
+
+// 0: Use standard Freeverb
+// 1: Use AReverb (currently not properly tuned)
+#define MT32EMU_USE_AREVERBMODEL 0
+
+namespace MT32Emu
+{
+// The higher this number, the more memory will be used, but the more samples can be processed in one run -
+// various parts of sample generation can be processed more efficiently in a single run.
+// A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever
+// called with will give no gain (but simply waste the memory).
+// Note that this value does *not* in any way impose limitations on the length given to render(), and has no effect
+// on the generated audio.
+// This value must be >= 1.
+const unsigned int MAX_SAMPLES_PER_RUN = 4096;
-#include "freeverb.h"
+// This determines the amount of memory available for simulating delays.
+// If set too low, partials aborted to allow other partials to play will not end gracefully, but will terminate
+// abruptly and potentially cause a pop/crackle in the audio output.
+// This value must be >= 1.
+const unsigned int MAX_PRERENDER_SAMPLES = 1024;
+}
-#include "structures.h"
-#include "i386.h"
-#include "mt32_file.h"
-#include "tables.h"
-#include "partial.h"
-#include "partialManager.h"
-#include "part.h"
-#include "synth.h"
+#include "Structures.h"
+#include "common/file.h"
+#include "Tables.h"
+#include "Poly.h"
+#include "LA32Ramp.h"
+#include "TVA.h"
+#include "TVP.h"
+#include "TVF.h"
+#include "Partial.h"
+#include "Part.h"
+#include "Synth.h"
#endif
diff --git a/audio/softsynth/mt32/part.cpp b/audio/softsynth/mt32/part.cpp
deleted file mode 100644
index 9f9269cba5..0000000000
--- a/audio/softsynth/mt32/part.cpp
+++ /dev/null
@@ -1,633 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include <string.h>
-#include <math.h>
-
-#include "mt32emu.h"
-
-namespace MT32Emu {
-
-static const Bit8u PartialStruct[13] = {
- 0, 0, 2, 2, 1, 3,
- 3, 0, 3, 0, 2, 1, 3 };
-
-static const Bit8u PartialMixStruct[13] = {
- 0, 1, 0, 1, 1, 0,
- 1, 3, 3, 2, 2, 2, 2 };
-
-static const float floatKeyfollow[17] = {
- -1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f,
- 1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f,
- 5.0f/4.0f, 3.0f/2.0f, 2.0f,
- 1.0009765625f, 1.0048828125f
-};
-
-//FIXME:KG: Put this dpoly stuff somewhere better
-bool dpoly::isActive() const {
- return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL;
-}
-
-Bit32u dpoly::getAge() const {
- for (int i = 0; i < 4; i++) {
- if (partials[i] != NULL) {
- return partials[i]->age;
- }
- }
- return 0;
-}
-
-RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) {
- strcpy(name, "Rhythm");
- rhythmTemp = &synth->mt32ram.rhythmSettings[0];
- refresh();
-}
-
-Part::Part(Synth *useSynth, unsigned int usePartNum) {
- this->synth = useSynth;
- this->partNum = usePartNum;
- patchCache[0].dirty = true;
- holdpedal = false;
- patchTemp = &synth->mt32ram.patchSettings[partNum];
- if (usePartNum == 8) {
- // Nasty hack for rhythm
- timbreTemp = NULL;
- } else {
- sprintf(name, "Part %d", partNum + 1);
- timbreTemp = &synth->mt32ram.timbreSettings[partNum];
- }
- currentInstr[0] = 0;
- currentInstr[10] = 0;
- expression = 127;
- volumeMult = 0;
- volumesetting.leftvol = 32767;
- volumesetting.rightvol = 32767;
- bend = 0.0f;
- memset(polyTable,0,sizeof(polyTable));
- memset(patchCache, 0, sizeof(patchCache));
-}
-
-void Part::setHoldPedal(bool pedalval) {
- if (holdpedal && !pedalval) {
- holdpedal = false;
- stopPedalHold();
- } else {
- holdpedal = pedalval;
- }
-}
-
-void RhythmPart::setBend(unsigned int midiBend) {
- synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend);
- return;
-}
-
-void Part::setBend(unsigned int midiBend) {
- // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, center 0.0 and max 1.0
- if (midiBend <= 0x2000) {
- bend = ((signed int)midiBend - 0x2000) / (float)0x2000;
- } else {
- bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF;
- }
- // Loop through all partials to update their bend
- for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
- for (int j = 0; j < 4; j++) {
- if (polyTable[i].partials[j] != NULL) {
- polyTable[i].partials[j]->setBend(bend);
- }
- }
- }
-}
-
-void RhythmPart::setModulation(unsigned int midiModulation) {
- synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation);
-}
-
-void Part::setModulation(unsigned int midiModulation) {
- // Just a bloody guess, as always, before I get things figured out
- for (int t = 0; t < 4; t++) {
- if (patchCache[t].playPartial) {
- int newrate = (patchCache[t].modsense * midiModulation) >> 7;
- //patchCache[t].lfoperiod = lfotable[newrate];
- patchCache[t].lfodepth = newrate;
- //FIXME:KG: timbreTemp->partial[t].lfo.depth =
- }
- }
-}
-
-void RhythmPart::refresh() {
- updateVolume();
- // (Re-)cache all the mapped timbres ahead of time
- for (unsigned int drumNum = 0; drumNum < synth->controlROMMap->rhythmSettingsCount; drumNum++) {
- int drumTimbreNum = rhythmTemp[drumNum].timbre;
- if (drumTimbreNum >= 127) // 94 on MT-32
- continue;
- Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14...
- // FIXME:KG: Panning cache should be backed up to partials using it, too
- if (pan < 7) {
- drumPan[drumNum].leftvol = pan * 4681;
- drumPan[drumNum].rightvol = 32767;
- } else {
- drumPan[drumNum].rightvol = (14 - pan) * 4681;
- drumPan[drumNum].leftvol = 32767;
- }
- PatchCache *cache = drumCache[drumNum];
- backupCacheToPartials(cache);
- for (int t = 0; t < 4; t++) {
- // Common parameters, stored redundantly
- cache[t].dirty = true;
- cache[t].pitchShift = 0.0f;
- cache[t].benderRange = 0.0f;
- cache[t].pansetptr = &drumPan[drumNum];
- cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0;
- }
- }
-}
-
-void Part::refresh() {
- updateVolume();
- backupCacheToPartials(patchCache);
- for (int t = 0; t < 4; t++) {
- // Common parameters, stored redundantly
- patchCache[t].dirty = true;
- patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f;
- patchCache[t].benderRange = patchTemp->patch.benderRange;
- patchCache[t].pansetptr = &volumesetting;
- patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
- }
- memcpy(currentInstr, timbreTemp->common.name, 10);
-}
-
-const char *Part::getCurrentInstr() const {
- return &currentInstr[0];
-}
-
-void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
- for (int m = 0; m < 85; m++) {
- if (rhythmTemp[m].timbre == absTimbreNum - 128)
- drumCache[m][0].dirty = true;
- }
-}
-
-void Part::refreshTimbre(unsigned int absTimbreNum) {
- if (getAbsTimbreNum() == absTimbreNum) {
- memcpy(currentInstr, timbreTemp->common.name, 10);
- patchCache[0].dirty = true;
- }
-}
-
-int Part::fixBiaslevel(int srcpnt, int *dir) {
- int noteat = srcpnt & 0x3F;
- int outnote;
- if (srcpnt < 64)
- *dir = 0;
- else
- *dir = 1;
- outnote = 33 + noteat;
- //synth->printDebug("Bias note %d, dir %d", outnote, *dir);
-
- return outnote;
-}
-
-int Part::fixKeyfollow(int srckey) {
- if (srckey>=0 && srckey<=16) {
- int keyfix[17] = { -256*16, -128*16, -64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116};
- return keyfix[srckey];
- } else {
- //LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey);
- return 256;
- }
-}
-
-void Part::abortPoly(dpoly *poly) {
- if (!poly->isPlaying) {
- return;
- }
- for (int i = 0; i < 4; i++) {
- Partial *partial = poly->partials[i];
- if (partial != NULL) {
- partial->deactivate();
- }
- }
- poly->isPlaying = false;
-}
-
-void Part::setPatch(const PatchParam *patch) {
- patchTemp->patch = *patch;
-}
-
-void RhythmPart::setTimbre(TimbreParam * /*timbre*/) {
- synth->printDebug("%s: Attempted to call setTimbre() - doesn't make sense for rhythm", name);
-}
-
-void Part::setTimbre(TimbreParam *timbre) {
- *timbreTemp = *timbre;
-}
-
-unsigned int RhythmPart::getAbsTimbreNum() const {
- synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm", name);
- return 0;
-}
-
-unsigned int Part::getAbsTimbreNum() const {
- return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
-}
-
-void RhythmPart::setProgram(unsigned int patchNum) {
- synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum);
-}
-
-void Part::setProgram(unsigned int patchNum) {
- setPatch(&synth->mt32ram.patches[patchNum]);
- setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre);
-
- refresh();
-
- allSoundOff(); //FIXME:KG: Is this correct?
-}
-
-void Part::backupCacheToPartials(PatchCache cache[4]) {
- // check if any partials are still playing with the old patch cache
- // if so then duplicate the cached data from the part to the partial so that
- // we can change the part's cache without affecting the partial.
- // We delay this until now to avoid a copy operation with every note played
- for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
- for (int i = 0; i < 4; i++) {
- Partial *partial = polyTable[m].partials[i];
- if (partial != NULL && partial->patchCache == &cache[i]) {
- partial->cachebackup = cache[i];
- partial->patchCache = &partial->cachebackup;
- }
- }
- }
-}
-
-void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
- backupCacheToPartials(cache);
- int partialCount = 0;
- for (int t = 0; t < 4; t++) {
- cache[t].PCMPartial = false;
- if (((timbre->common.pmute >> t) & 0x1) == 1) {
- cache[t].playPartial = true;
- partialCount++;
- } else {
- cache[t].playPartial = false;
- continue;
- }
-
- // Calculate and cache common parameters
-
- cache[t].pcm = timbre->partial[t].wg.pcmwave;
- cache[t].useBender = (timbre->partial[t].wg.bender == 1);
-
- switch (t) {
- case 0:
- cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false;
- cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
- cache[t].structurePosition = 0;
- cache[t].structurePair = 1;
- break;
- case 1:
- cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false;
- cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
- cache[t].structurePosition = 1;
- cache[t].structurePair = 0;
- break;
- case 2:
- cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false;
- cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
- cache[t].structurePosition = 0;
- cache[t].structurePair = 3;
- break;
- case 3:
- cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false;
- cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
- cache[t].structurePosition = 1;
- cache[t].structurePair = 2;
- break;
- default:
- break;
- }
-
- cache[t].waveform = timbre->partial[t].wg.waveform;
- cache[t].pulsewidth = timbre->partial[t].wg.pulsewid;
- cache[t].pwsens = timbre->partial[t].wg.pwvelo;
- if (timbre->partial[t].wg.keyfollow > 16) {
- synth->printDebug("Bad keyfollow value in timbre!");
- cache[t].pitchKeyfollow = 1.0f;
- } else {
- cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow];
- }
-
- cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f;
- cache[t].pitchEnv = timbre->partial[t].env;
- cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f);
- cache[t].pitchsustain = cache[t].pitchEnv.level[3];
-
- // Calculate and cache TVA envelope stuff
- cache[t].ampEnv = timbre->partial[t].tva;
- cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f);
-
- cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]);
- cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1;
- cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]);
- cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2;
- cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf;
-
- // Calculate and cache filter stuff
- cache[t].filtEnv = timbre->partial[t].tvf;
- cache[t].filtkeyfollow = fixKeyfollow(cache[t].filtEnv.keyfollow);
- cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27);
- cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir);
- cache[t].tvfblevel = cache[t].filtEnv.biaslevel;
- cache[t].filtsustain = cache[t].filtEnv.envlevel[3];
-
- // Calculate and cache LFO stuff
- cache[t].lfodepth = timbre->partial[t].lfo.depth;
- cache[t].lfoperiod = synth->tables.lfoPeriod[(int)timbre->partial[t].lfo.rate];
- cache[t].lforate = timbre->partial[t].lfo.rate;
- cache[t].modsense = timbre->partial[t].lfo.modsense;
- }
- for (int t = 0; t < 4; t++) {
- // Common parameters, stored redundantly
- cache[t].dirty = false;
- cache[t].partialCount = partialCount;
- cache[t].sustain = (timbre->common.nosustain == 0);
- }
- //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform);
-
-#if MT32EMU_MONITOR_INSTRUMENTS == 1
- synth->printDebug("%s (%s): Recached timbre", name, currentInstr);
- for (int i = 0; i < 4; i++) {
- synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform);
- }
-#endif
-}
-
-const char *Part::getName() const {
- return name;
-}
-
-void Part::updateVolume() {
- volumeMult = synth->tables.volumeMult[patchTemp->outlevel * expression / 127];
-}
-
-int Part::getVolume() const {
- // FIXME: Use the mappings for this in the control ROM
- return patchTemp->outlevel * 127 / 100;
-}
-
-void Part::setVolume(int midiVolume) {
- // FIXME: Use the mappings for this in the control ROM
- patchTemp->outlevel = (Bit8u)(midiVolume * 100 / 127);
- updateVolume();
- synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume);
-}
-
-void Part::setExpression(int midiExpression) {
- expression = midiExpression;
- updateVolume();
-}
-
-void RhythmPart::setPan(unsigned int midiPan)
-{
- // FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct?
- synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan);
-}
-
-void Part::setPan(unsigned int midiPan) {
- // FIXME:KG: Tweaked this a bit so that we have a left 100%, center and right 100%
- // (But this makes the range somewhat skewed)
- // Check against the real thing
- // NOTE: Panning is inverted compared to GM.
- if (midiPan < 64) {
- volumesetting.leftvol = (Bit16s)(midiPan * 512);
- volumesetting.rightvol = 32767;
- } else if (midiPan == 64) {
- volumesetting.leftvol = 32767;
- volumesetting.rightvol = 32767;
- } else {
- volumesetting.rightvol = (Bit16s)((127 - midiPan) * 520);
- volumesetting.leftvol = 32767;
- }
- patchTemp->panpot = (Bit8u)(midiPan * 14 / 127);
- //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
-}
-
-void RhythmPart::playNote(unsigned int key, int vel) {
- if (key < 24 || key > 108)/*> 87 on MT-32)*/ {
- synth->printDebug("%s: Attempted to play invalid key %d", name, key);
- return;
- }
- int drumNum = key - 24;
- int drumTimbreNum = rhythmTemp[drumNum].timbre;
- if (drumTimbreNum >= 127) { // 94 on MT-32
- synth->printDebug("%s: Attempted to play unmapped key %d", name, key);
- return;
- }
- int absTimbreNum = drumTimbreNum + 128;
- TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre;
- memcpy(currentInstr, timbre->common.name, 10);
-#if MT32EMU_MONITOR_INSTRUMENTS == 1
- synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d", name, currentInstr, drumNum, absTimbreNum, vel, key);
-#endif
- if (drumCache[drumNum][0].dirty) {
- cacheTimbre(drumCache[drumNum], timbre);
- }
- playPoly(drumCache[drumNum], key, MIDDLEC, vel);
-}
-
-void Part::playNote(unsigned int key, int vel) {
- int freqNum = key;
- if (freqNum < 12) {
- synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key);
- freqNum += 12;
- } else if (freqNum > 108) {
- synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key);
- while (freqNum > 108) {
- freqNum -= 12;
- }
- }
- // POLY1 mode, Single Assign
- // Haven't found any software that uses any of the other poly modes
- // FIXME:KG: Should this also apply to rhythm?
- for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) {
- if (polyTable[i].isActive() && (polyTable[i].key == key)) {
- //AbortPoly(&polyTable[i]);
- stopNote(key);
- break;
- }
- }
-#if MT32EMU_MONITOR_INSTRUMENTS == 1
- synth->printDebug("%s (%s): starting poly - Vel %d Key %d", name, currentInstr, vel, key);
-#endif
- if (patchCache[0].dirty) {
- cacheTimbre(patchCache, timbreTemp);
- }
- playPoly(patchCache, key, freqNum, vel);
-}
-
-void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) {
- unsigned int needPartials = cache[0].partialCount;
- unsigned int freePartials = synth->partialManager->getFreePartialCount();
-
- if (freePartials < needPartials) {
- if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) {
- synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount());
- return;
- }
- }
- // Find free poly
- int m;
- for (m = 0; m < MT32EMU_MAX_POLY; m++) {
- if (!polyTable[m].isActive()) {
- break;
- }
- }
- if (m == MT32EMU_MAX_POLY) {
- synth->printDebug("%s (%s): No free poly to play key %d (vel %d)", name, currentInstr, key, vel);
- return;
- }
-
- dpoly *tpoly = &polyTable[m];
-
- tpoly->isPlaying = true;
- tpoly->key = key;
- tpoly->isDecay = false;
- tpoly->freqnum = freqNum;
- tpoly->vel = vel;
- tpoly->pedalhold = false;
-
- bool allnull = true;
- for (int x = 0; x < 4; x++) {
- if (cache[x].playPartial) {
- tpoly->partials[x] = synth->partialManager->allocPartial(partNum);
- allnull = false;
- } else {
- tpoly->partials[x] = NULL;
- }
- }
-
- if (allnull)
- synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr);
-
- tpoly->sustain = cache[0].sustain;
- tpoly->volumeptr = &volumeMult;
-
- for (int x = 0; x < 4; x++) {
- if (tpoly->partials[x] != NULL) {
- tpoly->partials[x]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]);
- tpoly->partials[x]->setBend(bend);
- }
- }
-}
-
-static void startDecayPoly(dpoly *tpoly) {
- if (tpoly->isDecay) {
- return;
- }
- tpoly->isDecay = true;
-
- for (int t = 0; t < 4; t++) {
- Partial *partial = tpoly->partials[t];
- if (partial == NULL)
- continue;
- partial->startDecayAll();
- }
- tpoly->isPlaying = false;
-}
-
-void Part::allNotesOff() {
- // Note: Unchecked on real MT-32, but the MIDI specification states that all notes off (0x7B)
- // should treat the hold pedal as usual.
- // All *sound* off (0x78) should stop notes immediately regardless of the hold pedal.
- // The latter controller is not implemented on the MT-32 (according to the docs).
- for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
- dpoly *tpoly = &polyTable[q];
- if (tpoly->isPlaying) {
- if (holdpedal)
- tpoly->pedalhold = true;
- else if (tpoly->sustain)
- startDecayPoly(tpoly);
- }
- }
-}
-
-void Part::allSoundOff() {
- for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
- dpoly *tpoly = &polyTable[q];
- if (tpoly->isPlaying) {
- startDecayPoly(tpoly);
- }
- }
-}
-
-void Part::stopPedalHold() {
- for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
- dpoly *tpoly;
- tpoly = &polyTable[q];
- if (tpoly->isActive() && tpoly->pedalhold)
- stopNote(tpoly->key);
- }
-}
-
-void Part::stopNote(unsigned int key) {
- // Non-sustaining instruments ignore stop commands.
- // They die away eventually anyway
-
-#if MT32EMU_MONITOR_INSTRUMENTS == 1
- synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key);
-#endif
-
- if (key != 255) {
- for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
- dpoly *tpoly = &polyTable[q];
- if (tpoly->isPlaying && tpoly->key == key) {
- if (holdpedal)
- tpoly->pedalhold = true;
- else if (tpoly->sustain)
- startDecayPoly(tpoly);
- }
- }
- return;
- }
-
- // Find oldest poly... yes, the MT-32 can be reconfigured to kill different poly first
- // This is simplest
- int oldest = -1;
- Bit32u oldage = 0;
-
- for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
- dpoly *tpoly = &polyTable[q];
-
- if (tpoly->isPlaying && !tpoly->isDecay) {
- if (tpoly->getAge() >= oldage) {
- oldage = tpoly->getAge();
- oldest = q;
- }
- }
- }
-
- if (oldest != -1) {
- startDecayPoly(&polyTable[oldest]);
- }
-}
-
-}
diff --git a/audio/softsynth/mt32/part.h b/audio/softsynth/mt32/part.h
deleted file mode 100644
index 967298258c..0000000000
--- a/audio/softsynth/mt32/part.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef MT32EMU_PART_H
-#define MT32EMU_PART_H
-
-namespace MT32Emu {
-
-class Synth;
-
-class Part {
-private:
- // Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)
- MemParams::PatchTemp *patchTemp;
- TimbreParam *timbreTemp;
-
- // 0=Part 1, .. 7=Part 8, 8=Rhythm
- unsigned int partNum;
-
- bool holdpedal;
-
- StereoVolume volumesetting;
-
- PatchCache patchCache[4];
-
- float bend; // -1.0 .. +1.0
-
- dpoly polyTable[MT32EMU_MAX_POLY];
-
- void abortPoly(dpoly *poly);
-
- static int fixKeyfollow(int srckey);
- static int fixBiaslevel(int srcpnt, int *dir);
-
- void setPatch(const PatchParam *patch);
-
-protected:
- Synth *synth;
- char name[8]; // "Part 1".."Part 8", "Rhythm"
- char currentInstr[11];
- int expression;
- Bit32u volumeMult;
-
- void updateVolume();
- void backupCacheToPartials(PatchCache cache[4]);
- void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre);
- void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel);
- const char *getName() const;
-
-public:
- Part(Synth *synth, unsigned int usePartNum);
- virtual ~Part() {}
- virtual void playNote(unsigned int key, int vel);
- void stopNote(unsigned int key);
- void allNotesOff();
- void allSoundOff();
- int getVolume() const;
- void setVolume(int midiVolume);
- void setExpression(int midiExpression);
- virtual void setPan(unsigned int midiPan);
- virtual void setBend(unsigned int midiBend);
- virtual void setModulation(unsigned int midiModulation);
- virtual void setProgram(unsigned int midiProgram);
- void setHoldPedal(bool pedalval);
- void stopPedalHold();
- virtual void refresh();
- virtual void refreshTimbre(unsigned int absTimbreNum);
- virtual void setTimbre(TimbreParam *timbre);
- virtual unsigned int getAbsTimbreNum() const;
- const char *getCurrentInstr() const;
-};
-
-class RhythmPart: public Part {
- // Pointer to the area of the MT-32's memory dedicated to rhythm
- const MemParams::RhythmTemp *rhythmTemp;
-
- // This caches the timbres/settings in use by the rhythm part
- PatchCache drumCache[85][4];
- StereoVolume drumPan[85];
-public:
- RhythmPart(Synth *synth, unsigned int usePartNum);
- void refresh();
- void refreshTimbre(unsigned int timbreNum);
- void setTimbre(TimbreParam *timbre);
- void playNote(unsigned int key, int vel);
- unsigned int getAbsTimbreNum() const;
- void setPan(unsigned int midiPan);
- void setBend(unsigned int midiBend);
- void setModulation(unsigned int midiModulation);
- void setProgram(unsigned int patchNum);
-};
-
-}
-#endif
diff --git a/audio/softsynth/mt32/partial.cpp b/audio/softsynth/mt32/partial.cpp
deleted file mode 100644
index c4f2e94ebe..0000000000
--- a/audio/softsynth/mt32/partial.cpp
+++ /dev/null
@@ -1,968 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include <stdlib.h>
-#include <math.h>
-#include <string.h>
-
-#include "mt32emu.h"
-
-#if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__)
-// Older versions of Mac OS X didn't supply a powf function, so using it
-// will cause a binary incompatibility when trying to run a binary built
-// on a newer OS X release on an older one. And Solaris 8 doesn't provide
-// powf, floorf, fabsf etc. at all.
-// Cross-compiled MinGW32 toolchains suffer from a cross-compile bug in
-// libstdc++. math/stubs.o should be empty, but it comes with a symbol for
-// powf, resulting in a linker error because of multiple definitions.
-// Hence we re-define them here. The only potential drawback is that it
-// might be a little bit slower this way.
-#define powf(x,y) ((float)pow(x,y))
-#define floorf(x) ((float)floor(x))
-#define fabsf(x) ((float)fabs(x))
-#endif
-
-#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y)))
-#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y)))
-#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point)
-#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point))
-
-using namespace MT32Emu;
-
-Partial::Partial(Synth *useSynth) {
- this->synth = useSynth;
- ownerPart = -1;
- poly = NULL;
- pair = NULL;
-#if MT32EMU_ACCURATENOTES == 1
- for (int i = 0; i < 3; i++) {
- noteLookupStorage.waveforms[i] = new Bit16s[65536];
- }
- noteLookup = &noteLookupStorage;
-#endif
-}
-
-Partial::~Partial() {
-#if MT32EMU_ACCURATENOTES == 1
- for (int i = 0; i < 3; i++) {
- delete[] noteLookupStorage.waveforms[i];
- }
- delete[] noteLookupStorage.wavTable;
-#endif
-}
-
-int Partial::getOwnerPart() const {
- return ownerPart;
-}
-
-bool Partial::isActive() {
- return ownerPart > -1;
-}
-
-const dpoly *Partial::getDpoly() const {
- return this->poly;
-}
-
-void Partial::activate(int part) {
- // This just marks the partial as being assigned to a part
- ownerPart = part;
-}
-
-void Partial::deactivate() {
- ownerPart = -1;
- if (poly != NULL) {
- for (int i = 0; i < 4; i++) {
- if (poly->partials[i] == this) {
- poly->partials[i] = NULL;
- break;
- }
- }
- if (pair != NULL) {
- pair->pair = NULL;
- }
- }
-}
-
-void Partial::initKeyFollow(int key) {
- // Setup partial keyfollow
- // Note follow relative to middle C
-
- // Calculate keyfollow for pitch
-#if 1
- float rel = key == -1 ? 0.0f : (key - MIDDLEC);
- float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift;
- //FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)?
- //int newKey = (int)(rel * patchCache->pitchKeyfollow);
- //float newPitch = newKey + patchCache->pitch + patchCache->pitchShift;
-#else
- float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC);
- float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch;
-#endif
-#if MT32EMU_ACCURATENOTES == 1
- noteVal = newPitch;
- synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, (double)patchCache->pitch, (double)patchCache->pitchKeyfollow, (double)patchCache->pitchShift, (double)newPitch);
-#else
- float newPitchInt;
- float newPitchFract = modff(newPitch, &newPitchInt);
- if (newPitchFract > 0.5f) {
- newPitchInt += 1.0f;
- newPitchFract -= 1.0f;
- }
- noteVal = (int)newPitchInt;
- fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f);
- synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, (double)patchCache->pitch, (double)patchCache->pitchKeyfollow, (double)patchCache->pitchShift, (double)newPitch, noteVal, fineShift);
-#endif
- // FIXME:KG: Raise/lower by octaves until in the supported range.
- while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108?
- noteVal -= 12;
- while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12?
- noteVal += 12;
- // Calculate keyfollow for filter
- int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096;
- if (keyfollow > 108)
- keyfollow = 108;
- else if (keyfollow < -108)
- keyfollow = -108;
- filtVal = synth->tables.tvfKeyfollowMult[keyfollow + 108];
- realVal = synth->tables.tvfKeyfollowMult[(noteVal - MIDDLEC) + 108];
-}
-
-int Partial::getKey() const {
- if (poly == NULL) {
- return -1;
- } else {
- return poly->key;
- }
-}
-
-void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) {
- if (usePoly == NULL || useCache == NULL) {
- synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK");
- return;
- }
- patchCache = useCache;
- poly = usePoly;
- mixType = patchCache->structureMix;
- structurePosition = patchCache->structurePosition;
-
- play = true;
- initKeyFollow(poly->freqnum); // Initializes noteVal, filtVal and realVal
-#if MT32EMU_ACCURATENOTES == 0
- noteLookup = &synth->tables.noteLookups[noteVal - LOWEST_NOTE];
-#else
- Tables::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->pcmWaves, NULL);
-#endif
- keyLookup = &synth->tables.keyLookups[poly->freqnum - 12];
-
- if (patchCache->PCMPartial) {
- pcmNum = patchCache->pcm;
- if (synth->controlROMMap->pcmCount > 128) {
- // CM-32L, etc. support two "banks" of PCMs, selectable by waveform type parameter.
- if (patchCache->waveform > 1) {
- pcmNum += 128;
- }
- }
- pcmWave = &synth->pcmWaves[pcmNum];
- } else {
- pcmWave = NULL;
- }
-
- lfoPos = 0;
- pulsewidth = patchCache->pulsewidth + synth->tables.pwVelfollowAdd[patchCache->pwsens][poly->vel];
- if (pulsewidth > 100) {
- pulsewidth = 100;
- } else if (pulsewidth < 0) {
- pulsewidth = 0;
- }
-
- for (int e = 0; e < 3; e++) {
- envs[e].envpos = 0;
- envs[e].envstat = -1;
- envs[e].envbase = 0;
- envs[e].envdist = 0;
- envs[e].envsize = 0;
- envs[e].sustaining = false;
- envs[e].decaying = false;
- envs[e].prevlevel = 0;
- envs[e].counter = 0;
- envs[e].count = 0;
- }
- ampEnvVal = 0;
- pitchEnvVal = 0;
- pitchSustain = false;
- loopPos = 0;
- partialOff.pcmoffset = partialOff.pcmplace = 0;
- pair = pairPartial;
- useNoisePair = pairPartial == NULL && (mixType == 1 || mixType == 2);
- age = 0;
- alreadyOutputed = false;
- memset(history,0,sizeof(history));
-}
-
-Bit16s *Partial::generateSamples(long length) {
- if (!isActive() || alreadyOutputed) {
- return NULL;
- }
- if (poly == NULL) {
- synth->printDebug("*** ERROR: poly is NULL at Partial::generateSamples()!");
- return NULL;
- }
-
- alreadyOutputed = true;
-
- // Generate samples
-
- Bit16s *partialBuf = &myBuffer[0];
- Bit32u volume = *poly->volumeptr;
- while (length--) {
- Bit32s envval;
- Bit32s sample = 0;
- if (!envs[EnvelopeType_amp].sustaining) {
- if (envs[EnvelopeType_amp].count <= 0) {
- Bit32u ampval = getAmpEnvelope();
- if (!play) {
- deactivate();
- break;
- }
- if (ampval > 100) {
- ampval = 100;
- }
-
- ampval = synth->tables.volumeMult[ampval];
- ampval = FIXEDPOINT_UMULT(ampval, synth->tables.tvaVelfollowMult[poly->vel][(int)patchCache->ampEnv.velosens], 8);
- //if (envs[EnvelopeType_amp].sustaining)
- ampEnvVal = ampval;
- }
- --envs[EnvelopeType_amp].count;
- }
-
- unsigned int lfoShift = 0x1000;
- if (pitchSustain) {
- // Calculate LFO position
- // LFO does not kick in completely until pitch envelope sustains
- if (patchCache->lfodepth > 0) {
- lfoPos++;
- if (lfoPos >= patchCache->lfoperiod)
- lfoPos = 0;
- int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16);
- int lfoatr = synth->tables.sintable[lfoatm];
- lfoShift = synth->tables.lfoShift[patchCache->lfodepth][lfoatr];
- }
- } else {
- // Calculate Pitch envelope
- envval = getPitchEnvelope();
- int pd = patchCache->pitchEnv.depth;
- pitchEnvVal = synth->tables.pitchEnvVal[pd][envval];
- }
-
- int delta;
-
- // Wrap positions or end if necessary
- if (patchCache->PCMPartial) {
- // PCM partial
-
- delta = noteLookup->wavTable[pcmNum];
- int len = pcmWave->len;
- if (partialOff.pcmplace >= len) {
- if (pcmWave->loop) {
- //partialOff.pcmplace = partialOff.pcmoffset = 0;
- partialOff.pcmplace %= len;
- } else {
- play = false;
- deactivate();
- break;
- }
- }
- } else {
- // Synthesis partial
- delta = 0x10000;
- partialOff.pcmplace %= (Bit16u)noteLookup->div2;
- }
-
- // Build delta for position of next sample
- // Fix delta code
- Bit32u tdelta = delta;
-#if MT32EMU_ACCURATENOTES == 0
- tdelta = FIXEDPOINT_UMULT(tdelta, fineShift, 12);
-#endif
- tdelta = FIXEDPOINT_UMULT(tdelta, pitchEnvVal, 12);
- tdelta = FIXEDPOINT_UMULT(tdelta, lfoShift, 12);
- tdelta = FIXEDPOINT_UMULT(tdelta, bendShift, 12);
- delta = (int)tdelta;
-
- // Get waveform - either PCM or synthesized sawtooth or square
- if (ampEnvVal > 0) {
- if (patchCache->PCMPartial) {
- // Render PCM sample
- int ra, rb, dist;
- Bit32u taddr;
- Bit32u pcmAddr = pcmWave->addr;
- if (delta < 0x10000) {
- // Linear sound interpolation
- taddr = pcmAddr + partialOff.pcmplace;
- ra = synth->pcmROMData[taddr];
- taddr++;
- if (taddr == pcmAddr + pcmWave->len) {
- // Past end of PCM
- if (pcmWave->loop) {
- rb = synth->pcmROMData[pcmAddr];
- } else {
- rb = 0;
- }
- } else {
- rb = synth->pcmROMData[taddr];
- }
- dist = rb - ra;
- sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8));
- } else {
- // Sound decimation
- // The right way to do it is to use a lowpass filter on the waveform before selecting
- // a point. This is too slow. The following approximates this as fast as possible
- int idelta = delta >> 16;
- taddr = pcmAddr + partialOff.pcmplace;
- ra = synth->pcmROMData[taddr++];
- for (int ix = 0; ix < idelta - 1; ix++) {
- if (taddr == pcmAddr + pcmWave->len) {
- // Past end of PCM
- if (pcmWave->loop) {
- taddr = pcmAddr;
- } else {
- // Behave as if all subsequent samples were 0
- break;
- }
- }
- ra += synth->pcmROMData[taddr++];
- }
- sample = ra / idelta;
- }
- } else {
- // Render synthesised sample
- int toff = partialOff.pcmplace;
- int minorplace = partialOff.pcmoffset >> 14;
- Bit32s filterInput;
- Bit32s filtval = getFiltEnvelope();
-
- //synth->printDebug("Filtval: %d", filtval);
-
- if ((patchCache->waveform & 1) == 0) {
- // Square waveform. Made by combining two pregenerated bandlimited
- // sawtooth waveforms
- Bit32u ofsA = ((toff << 2) + minorplace) % noteLookup->waveformSize[0];
- int width = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[pulsewidth], 7);
- Bit32u ofsB = (ofsA + width) % noteLookup->waveformSize[0];
- Bit16s pa = noteLookup->waveforms[0][ofsA];
- Bit16s pb = noteLookup->waveforms[0][ofsB];
- filterInput = pa - pb;
- // Non-bandlimited squarewave
- /*
- ofs = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 8);
- if (toff < ofs)
- sample = 1 * WGAMP;
- else
- sample = -1 * WGAMP;
- */
- } else {
- // Sawtooth. Made by combining the full cosine and half cosine according
- // to how it looks on the MT-32. What it really does it takes the
- // square wave and multiplies it by a full cosine
- int waveoff = (toff << 2) + minorplace;
- if (toff < noteLookup->sawTable[pulsewidth])
- filterInput = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
- else
- filterInput = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
- // This is the correct way
- // Seems slow to me (though bandlimited) -- doesn't seem to
- // sound any better though
- /*
- //int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8;
-
- Bit32u ofs = toff % (noteLookup->div2 >> 1);
-
- Bit32u ofs3 = toff + FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 9);
- ofs3 = ofs3 % (noteLookup->div2 >> 1);
-
- pa = noteLookup->waveforms[0][ofs];
- pb = noteLookup->waveforms[0][ofs3];
- sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / 2;
- */
- }
-
- //Very exact filter
- if (filtval > ((FILTERGRAN * 15) / 16))
- filtval = ((FILTERGRAN * 15) / 16);
- sample = (Bit32s)(floorf((synth->iirFilter)((float)filterInput, &history[0], synth->tables.filtCoeff[filtval][(int)patchCache->filtEnv.resonance])) / synth->tables.resonanceFactor[patchCache->filtEnv.resonance]);
- if (sample < -32768) {
- synth->printDebug("Overdriven amplitude for %d: %d:=%d < -32768", patchCache->waveform, filterInput, sample);
- sample = -32768;
- }
- else if (sample > 32767) {
- synth->printDebug("Overdriven amplitude for %d: %d:=%d > 32767", patchCache->waveform, filterInput, sample);
- sample = 32767;
- }
- }
- }
-
- // Add calculated delta to our waveform offset
- Bit32u absOff = ((partialOff.pcmplace << 16) | partialOff.pcmoffset);
- absOff += delta;
- partialOff.pcmplace = (Bit16u)((absOff & 0xFFFF0000) >> 16);
- partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF);
-
- // Put volume envelope over generated sample
- sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9);
- sample = FIXEDPOINT_SMULT(sample, volume, 7);
- envs[EnvelopeType_amp].envpos++;
- envs[EnvelopeType_pitch].envpos++;
- envs[EnvelopeType_filt].envpos++;
-
- *partialBuf++ = (Bit16s)sample;
- }
- // We may have deactivated and broken out of the loop before the end of the buffer,
- // if so then fill the remainder with 0s.
- if (++length > 0)
- memset(partialBuf, 0, length * 2);
- return &myBuffer[0];
-}
-
-void Partial::setBend(float factor) {
- if (!patchCache->useBender || factor == 0.0f) {
- bendShift = 4096;
- return;
- }
- // NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB.
- // FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs.
- float bendSemitones = factor * patchCache->benderRange; // -24 .. 24
- float mult = powf(2.0f, bendSemitones / 12.0f);
- synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", (double)factor, (double)patchCache->benderRange, (double)bendSemitones, (double)mult);
- bendShift = (int)(mult * 4096.0f);
-}
-
-Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
- if (buf1 == NULL)
- return buf2;
- if (buf2 == NULL)
- return buf1;
-
- Bit16s *outBuf = buf1;
-#if MT32EMU_USE_MMX >= 1
- // KG: This seems to be fine
- int donelen = i386_mixBuffers(buf1, buf2, len);
- len -= donelen;
- buf1 += donelen;
- buf2 += donelen;
-#endif
- while (len--) {
- *buf1 = *buf1 + *buf2;
- buf1++, buf2++;
- }
- return outBuf;
-}
-
-Bit16s *Partial::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
- if (buf1 == NULL)
- return NULL;
- if (buf2 == NULL) {
- Bit16s *outBuf = buf1;
- while (len--) {
- if (*buf1 < -8192)
- *buf1 = -8192;
- else if (*buf1 > 8192)
- *buf1 = 8192;
- buf1++;
- }
- return outBuf;
- }
-
- Bit16s *outBuf = buf1;
-#if MT32EMU_USE_MMX >= 1
- // KG: This seems to be fine
- int donelen = i386_mixBuffersRingMix(buf1, buf2, len);
- len -= donelen;
- buf1 += donelen;
- buf2 += donelen;
-#endif
- while (len--) {
- float a, b;
- a = ((float)*buf1) / 8192.0f;
- b = ((float)*buf2) / 8192.0f;
- a = (a * b) + a;
- if (a > 1.0f)
- a = 1.0f;
- if (a < -1.0f)
- a = -1.0f;
- *buf1 = (Bit16s)(a * 8192.0f);
- buf1++;
- buf2++;
- //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i];
- }
- return outBuf;
-}
-
-Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
- if (buf1 == NULL) {
- return NULL;
- }
- if (buf2 == NULL) {
- return NULL;
- }
-
- Bit16s *outBuf = buf1;
-#if MT32EMU_USE_MMX >= 1
- // FIXME:KG: Not really checked as working
- int donelen = i386_mixBuffersRing(buf1, buf2, len);
- len -= donelen;
- buf1 += donelen;
- buf2 += donelen;
-#endif
- while (len--) {
- float a, b;
- a = ((float)*buf1) / 8192.0f;
- b = ((float)*buf2) / 8192.0f;
- a *= b;
- if (a > 1.0f)
- a = 1.0f;
- if (a < -1.0f)
- a = -1.0f;
- *buf1 = (Bit16s)(a * 8192.0f);
- buf1++;
- buf2++;
- }
- return outBuf;
-}
-
-void Partial::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) {
- if (buf2 == NULL) {
- while (len--) {
- *outBuf++ = *buf1++;
- *outBuf++ = 0;
- }
- } else if (buf1 == NULL) {
- while (len--) {
- *outBuf++ = 0;
- *outBuf++ = *buf2++;
- }
- } else {
- while (len--) {
- *outBuf++ = *buf1++;
- *outBuf++ = *buf2++;
- }
- }
-}
-
-bool Partial::produceOutput(Bit16s *partialBuf, long length) {
- if (!isActive() || alreadyOutputed)
- return false;
- if (poly == NULL) {
- synth->printDebug("*** ERROR: poly is NULL at Partial::produceOutput()!");
- return false;
- }
-
- Bit16s *pairBuf = NULL;
- // Check for dependant partial
- if (pair != NULL) {
- if (!pair->alreadyOutputed) {
- // Note: pair may have become NULL after this
- pairBuf = pair->generateSamples(length);
- }
- } else if (useNoisePair) {
- // Generate noise for pairless ring mix
- pairBuf = synth->tables.noiseBuf;
- }
-
- Bit16s *myBuf = generateSamples(length);
-
- if (myBuf == NULL && pairBuf == NULL)
- return false;
-
- Bit16s *p1buf, *p2buf;
-
- if (structurePosition == 0 || pairBuf == NULL) {
- p1buf = myBuf;
- p2buf = pairBuf;
- } else {
- p2buf = myBuf;
- p1buf = pairBuf;
- }
-
- //synth->printDebug("mixType: %d", mixType);
-
- Bit16s *mixedBuf;
- switch (mixType) {
- case 0:
- // Standard sound mix
- mixedBuf = mixBuffers(p1buf, p2buf, length);
- break;
-
- case 1:
- // Ring modulation with sound mix
- mixedBuf = mixBuffersRingMix(p1buf, p2buf, length);
- break;
-
- case 2:
- // Ring modulation alone
- mixedBuf = mixBuffersRing(p1buf, p2buf, length);
- break;
-
- case 3:
- // Stereo mixing. One partial to one speaker channel, one to another.
- // FIXME:KG: Surely we should be multiplying by the left/right volumes here?
- mixBuffersStereo(p1buf, p2buf, partialBuf, length);
- return true;
-
- default:
- mixedBuf = mixBuffers(p1buf, p2buf, length);
- break;
- }
-
- if (mixedBuf == NULL)
- return false;
-
- Bit16s leftvol, rightvol;
- leftvol = patchCache->pansetptr->leftvol;
- rightvol = patchCache->pansetptr->rightvol;
-
-#if MT32EMU_USE_MMX >= 2
- // FIXME:KG: This appears to introduce crackle
- int donelen = i386_partialProductOutput(length, leftvol, rightvol, partialBuf, mixedBuf);
- length -= donelen;
- mixedBuf += donelen;
- partialBuf += donelen * 2;
-#endif
- while (length--) {
- *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)leftvol) >> 15);
- *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)rightvol) >> 15);
- mixedBuf++;
- }
- return true;
-}
-
-Bit32s Partial::getFiltEnvelope() {
- int reshigh;
-
- int cutoff, depth;
-
- EnvelopeStatus *tStat = &envs[EnvelopeType_filt];
-
- if (tStat->decaying) {
- reshigh = tStat->envbase;
- reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
- if (tStat->envpos >= tStat->envsize)
- reshigh = 0;
- } else {
- if (tStat->envstat==4) {
- reshigh = patchCache->filtsustain;
- if (!poly->sustain) {
- startDecay(EnvelopeType_filt, reshigh);
- }
- } else {
- if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
- if (tStat->envstat==-1)
- tStat->envbase = 0;
- else
- tStat->envbase = patchCache->filtEnv.envlevel[tStat->envstat];
- tStat->envstat++;
- tStat->envpos = 0;
- if (tStat->envstat == 3) {
- tStat->envsize = synth->tables.envTime[(int)patchCache->filtEnv.envtime[tStat->envstat]];
- } else {
- Bit32u envTime = (int)patchCache->filtEnv.envtime[tStat->envstat];
- if (tStat->envstat > 1) {
- int envDiff = abs(patchCache->filtEnv.envlevel[tStat->envstat] - patchCache->filtEnv.envlevel[tStat->envstat - 1]);
- if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
- envTime = synth->tables.envDeltaMaxTime[envDiff];
- }
- }
-
- tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf]) >> 8;
- }
-
- tStat->envsize++;
- tStat->envdist = patchCache->filtEnv.envlevel[tStat->envstat] - tStat->envbase;
- }
-
- reshigh = tStat->envbase;
- reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
-
- }
- tStat->prevlevel = reshigh;
- }
-
- cutoff = patchCache->filtEnv.cutoff;
-
- //if (patchCache->waveform==1) reshigh = (reshigh * 3) >> 2;
-
- depth = patchCache->filtEnv.envdepth;
-
- //int sensedep = (depth * 127-patchCache->filtEnv.envsense) >> 7;
- depth = FIXEDPOINT_UMULT(depth, synth->tables.tvfVelfollowMult[poly->vel][(int)patchCache->filtEnv.envsense], 8);
-
- int bias = patchCache->tvfbias;
- int dist;
-
- if (bias != 0) {
- //FIXME:KG: Is this really based on pitch (as now), or key pressed?
- //synth->printDebug("Cutoff before %d", cutoff);
- if (patchCache->tvfdir == 0) {
- if (noteVal < bias) {
- dist = bias - noteVal;
- cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
- }
- } else {
- // > Bias
- if (noteVal > bias) {
- dist = noteVal - bias;
- cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
- }
-
- }
- //synth->printDebug("Cutoff after %d", cutoff);
- }
-
- depth = (depth * keyLookup->envDepthMult[patchCache->filtEnv.envdkf]) >> 8;
- reshigh = (reshigh * depth) >> 7;
-
- Bit32s tmp;
-
- cutoff *= filtVal;
- cutoff /= realVal; //FIXME:KG: With filter keyfollow 0, this makes no sense. What's correct?
-
- reshigh *= filtVal;
- reshigh /= realVal; //FIXME:KG: As above for cutoff
-
- if (patchCache->waveform == 1) {
- reshigh = (reshigh * 65) / 100;
- }
-
- if (cutoff > 100)
- cutoff = 100;
- else if (cutoff < 0)
- cutoff = 0;
- if (reshigh > 100)
- reshigh = 100;
- else if (reshigh < 0)
- reshigh = 0;
- tmp = noteLookup->nfiltTable[cutoff][reshigh];
- //tmp *= keyfollow;
- //tmp /= realfollow;
-
- //synth->printDebug("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256);
- return tmp;
-}
-
-bool Partial::shouldReverb() {
- if (!isActive())
- return false;
- return patchCache->reverb;
-}
-
-Bit32u Partial::getAmpEnvelope() {
- Bit32s tc;
-
- EnvelopeStatus *tStat = &envs[EnvelopeType_amp];
-
- if (!play)
- return 0;
-
- if (tStat->decaying) {
- tc = tStat->envbase;
- tc += (tStat->envdist * tStat->envpos) / tStat->envsize;
- if (tc < 0)
- tc = 0;
- if ((tStat->envpos >= tStat->envsize) || (tc == 0)) {
- play = false;
- // Don't have to worry about prevlevel storage or anything, this partial's about to die
- return 0;
- }
- } else {
- if ((tStat->envstat == -1) || (tStat->envpos >= tStat->envsize)) {
- if (tStat->envstat == -1)
- tStat->envbase = 0;
- else
- tStat->envbase = patchCache->ampEnv.envlevel[tStat->envstat];
- tStat->envstat++;
- tStat->envpos = 0;
- if (tStat->envstat == 4) {
- //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
- tc = patchCache->ampEnv.envlevel[3];
- if (!poly->sustain)
- startDecay(EnvelopeType_amp, tc);
- else
- tStat->sustaining = true;
- goto PastCalc;
- }
- Bit8u targetLevel = patchCache->ampEnv.envlevel[tStat->envstat];
- tStat->envdist = targetLevel - tStat->envbase;
- Bit32u envTime = patchCache->ampEnv.envtime[tStat->envstat];
- if (targetLevel == 0) {
- tStat->envsize = synth->tables.envDecayTime[envTime];
- } else {
- int envLevelDelta = abs(tStat->envdist);
- if (envTime > synth->tables.envDeltaMaxTime[envLevelDelta]) {
- envTime = synth->tables.envDeltaMaxTime[envLevelDelta];
- }
- tStat->envsize = synth->tables.envTime[envTime];
- }
-
- // Time keyfollow is used by all sections of the envelope (confirmed on CM-32L)
- tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
-
- switch (tStat->envstat) {
- case 0:
- //Spot for velocity time follow
- //Only used for first attack
- tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, synth->tables.envTimeVelfollowMult[(int)patchCache->ampEnv.envvkf][poly->vel], 8);
- //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
- break;
- case 1:
- case 2:
- case 3:
- //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
- break;
- default:
- synth->printDebug("Invalid TVA envelope number %d hit!", tStat->envstat);
- break;
- }
-
- tStat->envsize++;
-
- if (tStat->envdist != 0) {
- tStat->counter = abs(tStat->envsize / tStat->envdist);
- //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
- } else {
- tStat->counter = 0;
- //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
- }
- }
- tc = tStat->envbase;
- tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
- tStat->count = tStat->counter;
-PastCalc:
- tc = (tc * (Bit32s)patchCache->ampEnv.level) / 100;
- }
-
- // Prevlevel storage is bottle neck
- tStat->prevlevel = tc;
-
- //Bias level crap stuff now
-
- for (int i = 0; i < 2; i++) {
- if (patchCache->ampblevel[i]!=0) {
- int bias = patchCache->ampbias[i];
- if (patchCache->ampdir[i]==0) {
- // < Bias
- if (noteVal < bias) {
- int dist = bias - noteVal;
- tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
- }
- } else {
- // > Bias
- if (noteVal > bias) {
- int dist = noteVal - bias;
- tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
- }
- }
- }
- }
- if (tc < 0) {
- synth->printDebug("*** ERROR: tc < 0 (%d) at getAmpEnvelope()", tc);
- tc = 0;
- }
- return (Bit32u)tc;
-}
-
-Bit32s Partial::getPitchEnvelope() {
- EnvelopeStatus *tStat = &envs[EnvelopeType_pitch];
-
- Bit32s tc;
- pitchSustain = false;
- if (tStat->decaying) {
- if (tStat->envpos >= tStat->envsize)
- tc = patchCache->pitchEnv.level[4];
- else {
- tc = tStat->envbase;
- tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
- }
- } else {
- if (tStat->envstat==3) {
- tc = patchCache->pitchsustain;
- if (poly->sustain)
- pitchSustain = true;
- else
- startDecay(EnvelopeType_pitch, tc);
- } else {
- if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
- tStat->envstat++;
-
- tStat->envbase = patchCache->pitchEnv.level[tStat->envstat];
-
- Bit32u envTime = patchCache->pitchEnv.time[tStat->envstat];
- int envDiff = abs(patchCache->pitchEnv.level[tStat->envstat] - patchCache->pitchEnv.level[tStat->envstat + 1]);
- if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
- envTime = synth->tables.envDeltaMaxTime[envDiff];
- }
-
- tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;
-
- tStat->envpos = 0;
- tStat->envsize++;
- tStat->envdist = patchCache->pitchEnv.level[tStat->envstat + 1] - tStat->envbase;
- }
- tc = tStat->envbase;
- tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
- }
- tStat->prevlevel = tc;
- }
- return tc;
-}
-
-void Partial::startDecayAll() {
- startDecay(EnvelopeType_amp, envs[EnvelopeType_amp].prevlevel);
- startDecay(EnvelopeType_filt, envs[EnvelopeType_filt].prevlevel);
- startDecay(EnvelopeType_pitch, envs[EnvelopeType_pitch].prevlevel);
- pitchSustain = false;
-}
-
-void Partial::startDecay(EnvelopeType envnum, Bit32s startval) {
- EnvelopeStatus *tStat = &envs[envnum];
-
- tStat->sustaining = false;
- tStat->decaying = true;
- tStat->envpos = 0;
- tStat->envbase = startval;
-
- switch (envnum) {
- case EnvelopeType_amp:
- tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->ampEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
- tStat->envdist = -startval;
- break;
- case EnvelopeType_filt:
- tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->filtEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf], 8);
- tStat->envdist = -startval;
- break;
- case EnvelopeType_pitch:
- tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->pitchEnv.time[3]], keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow], 8);
- tStat->envdist = patchCache->pitchEnv.level[4] - startval;
- break;
- default:
- break;
- }
- tStat->envsize++;
-}
diff --git a/audio/softsynth/mt32/partial.h b/audio/softsynth/mt32/partial.h
deleted file mode 100644
index 93d8bcd985..0000000000
--- a/audio/softsynth/mt32/partial.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef MT32EMU_PARTIAL_H
-#define MT32EMU_PARTIAL_H
-
-namespace MT32Emu {
-
-class Synth;
-struct NoteLookup;
-
-enum EnvelopeType {
- EnvelopeType_amp = 0,
- EnvelopeType_filt = 1,
- EnvelopeType_pitch = 2
-};
-
-struct EnvelopeStatus {
- Bit32s envpos;
- Bit32s envstat;
- Bit32s envbase;
- Bit32s envdist;
- Bit32s envsize;
-
- bool sustaining;
- bool decaying;
- Bit32s prevlevel;
-
- Bit32s counter;
- Bit32s count;
-};
-
-// Class definition of MT-32 partials. 32 in all.
-class Partial {
-private:
- Synth *synth;
-
- int ownerPart; // -1 if unassigned
- int mixType;
- int structurePosition; // 0 or 1 of a structure pair
- bool useNoisePair;
-
- Bit16s myBuffer[MAX_SAMPLE_OUTPUT];
-
- // Keyfollowed note value
-#if MT32EMU_ACCURATENOTES == 1
- NoteLookup noteLookupStorage;
- float noteVal;
-#else
- int noteVal;
- int fineShift;
-#endif
- const NoteLookup *noteLookup; // LUTs for this noteVal
- const KeyLookup *keyLookup; // LUTs for the clamped (12..108) key
-
- // Keyfollowed filter values
- int realVal;
- int filtVal;
-
- // Only used for PCM partials
- int pcmNum;
- PCMWaveEntry *pcmWave;
-
- int pulsewidth;
-
- Bit32u lfoPos;
- soundaddr partialOff;
-
- Bit32u ampEnvVal;
- Bit32u pitchEnvVal;
-
- float history[32];
-
- bool pitchSustain;
-
- int loopPos;
-
- dpoly *poly;
-
- int bendShift;
-
- Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len);
- Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len);
- Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len);
- void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len);
-
- Bit32s getFiltEnvelope();
- Bit32u getAmpEnvelope();
- Bit32s getPitchEnvelope();
-
- void initKeyFollow(int freqNum);
-
-public:
- const PatchCache *patchCache;
- EnvelopeStatus envs[3];
- bool play;
-
- PatchCache cachebackup;
-
- Partial *pair;
- bool alreadyOutputed;
- Bit32u age;
-
- Partial(Synth *synth);
- ~Partial();
-
- int getOwnerPart() const;
- int getKey() const;
- const dpoly *getDpoly() const;
- bool isActive();
- void activate(int part);
- void deactivate(void);
- void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial);
- void startDecay(EnvelopeType envnum, Bit32s startval);
- void startDecayAll();
- void setBend(float factor);
- bool shouldReverb();
-
- // Returns true only if data written to buffer
- // This function (unlike the one below it) returns processed stereo samples
- // made from combining this single partial with its pair, if it has one.
- bool produceOutput(Bit16s * partialBuf, long length);
-
- // This function produces mono sample output using the partial's private internal buffer
- Bit16s *generateSamples(long length);
-};
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/partialManager.cpp b/audio/softsynth/mt32/partialManager.cpp
deleted file mode 100644
index 3d3b6302db..0000000000
--- a/audio/softsynth/mt32/partialManager.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include <string.h>
-
-#include "mt32emu.h"
-
-using namespace MT32Emu;
-
-PartialManager::PartialManager(Synth *useSynth) {
- this->synth = useSynth;
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
- partialTable[i] = new Partial(synth);
-}
-
-PartialManager::~PartialManager(void) {
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
- delete partialTable[i];
-}
-
-void PartialManager::getPerPartPartialUsage(int usage[9]) {
- memset(usage, 0, 9 * sizeof (int));
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialTable[i]->isActive())
- usage[partialTable[i]->getOwnerPart()]++;
- }
-}
-
-void PartialManager::clearAlreadyOutputed() {
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
- partialTable[i]->alreadyOutputed = false;
-}
-
-void PartialManager::ageAll() {
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
- partialTable[i]->age++;
-}
-
-bool PartialManager::shouldReverb(int i) {
- return partialTable[i]->shouldReverb();
-}
-
-bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) {
- return partialTable[i]->produceOutput(buffer, bufferLength);
-}
-
-void PartialManager::deactivateAll() {
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- partialTable[i]->deactivate();
- }
-}
-
-unsigned int PartialManager::setReserve(Bit8u *rset) {
- unsigned int pr = 0;
- for (int x = 0; x < 9; x++) {
- for (int y = 0; y < rset[x]; y++) {
- partialReserveTable[pr] = x;
- pr++;
- }
- }
- return pr;
-}
-
-Partial *PartialManager::allocPartial(int partNum) {
- Partial *outPartial = NULL;
-
- // Use the first inactive partial reserved for the specified part (if there are any)
- // Otherwise, use the last inactive partial, if any
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (!partialTable[i]->isActive()) {
- outPartial = partialTable[i];
- if (partialReserveTable[i] == partNum)
- break;
- }
- }
- if (outPartial != NULL) {
- outPartial->activate(partNum);
- outPartial->age = 0;
- }
- return outPartial;
-}
-
-unsigned int PartialManager::getFreePartialCount(void) {
- int count = 0;
- memset(partialPart, 0, sizeof(partialPart));
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (!partialTable[i]->isActive())
- count++;
- else
- partialPart[partialTable[i]->getOwnerPart()]++;
- }
- return count;
-}
-
-/*
-bool PartialManager::freePartials(unsigned int needed, int partNum) {
- int i;
- int myPartPrior = (int)mt32ram.system.reserveSettings[partNum];
- if (myPartPrior<partialPart[partNum]) {
- //This can have more parts, must kill off those with less priority
- int most, mostPart;
- while (needed > 0) {
- int selectPart = -1;
- //Find the worst offender with more partials than allocated and kill them
- most = -1;
- mostPart = -1;
- int diff;
-
- for (i=0;i<9;i++) {
- diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i];
-
- if (diff>0) {
- if (diff>most) {
- most = diff;
- mostPart = i;
- }
- }
- }
- selectPart = mostPart;
- if (selectPart == -1) {
- // All parts are within the allocated limits, you suck
- // Look for first partial not of this part that's decaying perhaps?
- return false;
- }
- bool found;
- int oldest;
- int oldnum;
- while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) {
- oldest = -1;
- oldnum = -1;
- found = false;
- for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialTable[i]->isActive) {
- if (partialTable[i]->ownerPart == selectPart) {
- found = true;
- if (partialTable[i]->age > oldest) {
- oldest = partialTable[i]->age;
- oldnum = i;
- }
- }
- }
- }
- if (!found) break;
- partialTable[oldnum]->deactivate();
- --partialPart[selectPart];
- --needed;
- }
-
- }
- return true;
-
- } else {
- //This part has reached its max, must kill off its own
- bool found;
- int oldest;
- int oldnum;
- while (needed > 0) {
- oldest = -1;
- oldnum = -1;
- found = false;
- for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialTable[i]->isActive) {
- if (partialTable[i]->ownerPart == partNum) {
- found = true;
- if (partialTable[i]->age > oldest) {
- oldest = partialTable[i]->age;
- oldnum = i;
- }
- }
- }
- }
- if (!found) break;
- partialTable[oldnum]->deactivate();
- --needed;
- }
- // Couldn't free enough partials, sorry
- if (needed>0) return false;
- return true;
- }
-
-}
-*/
-bool PartialManager::freePartials(unsigned int needed, int partNum) {
- if (needed == 0) {
- return true;
- }
- // Reclaim partials reserved for this part
- // Kill those that are already decaying first
- /*
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialReserveTable[i] == partNum) {
- if (partialTable[i]->ownerPart != partNum) {
- if (partialTable[i]->partCache->envs[AMPENV].decaying) {
- partialTable[i]->isActive = false;
- --needed;
- if (needed == 0)
- return true;
- }
- }
- }
- }*/
- // Then kill those with the lowest part priority -- oldest at the moment
- while (needed > 0) {
- Bit32u prior = 0;
- int priornum = -1;
-
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
- /*
- if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
- prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart];
- priornum = i;
- }*/
- if (partialTable[i]->age >= prior) {
- prior = partialTable[i]->age;
- priornum = i;
- }
- }
- }
- if (priornum != -1) {
- partialTable[priornum]->deactivate();
- --needed;
- } else {
- break;
- }
- }
-
- // Kill off the oldest partials within this part
- while (needed > 0) {
- Bit32u oldest = 0;
- int oldlist = -1;
- for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) {
- if (partialTable[i]->age >= oldest) {
- oldest = partialTable[i]->age;
- oldlist = i;
- }
- }
- }
- if (oldlist != -1) {
- partialTable[oldlist]->deactivate();
- --needed;
- } else {
- break;
- }
- }
- return needed == 0;
-}
-
-const Partial *PartialManager::getPartial(unsigned int partialNum) const {
- if (partialNum > MT32EMU_MAX_PARTIALS - 1)
- return NULL;
- return partialTable[partialNum];
-}
diff --git a/audio/softsynth/mt32/partialManager.h b/audio/softsynth/mt32/partialManager.h
deleted file mode 100644
index b10f93ff02..0000000000
--- a/audio/softsynth/mt32/partialManager.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef MT32EMU_PARTIALMANAGER_H
-#define MT32EMU_PARTIALMANAGER_H
-
-namespace MT32Emu {
-
-class Synth;
-
-class PartialManager {
-private:
- Synth *synth; // Only used for sending debug output
-
- Partial *partialTable[MT32EMU_MAX_PARTIALS];
- Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS];
- Bit32s partialPart[9]; // The count of partials played per part
-
-public:
-
- PartialManager(Synth *synth);
- ~PartialManager();
- Partial *allocPartial(int partNum);
- unsigned int getFreePartialCount(void);
- bool freePartials(unsigned int needed, int partNum);
- unsigned int setReserve(Bit8u *rset);
- void deactivateAll();
- void ageAll();
- bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength);
- bool shouldReverb(int i);
- void clearAlreadyOutputed();
- void getPerPartPartialUsage(int usage[9]);
- const Partial *getPartial(unsigned int partialNum) const;
-};
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/structures.h b/audio/softsynth/mt32/structures.h
deleted file mode 100644
index ef58c1d20f..0000000000
--- a/audio/softsynth/mt32/structures.h
+++ /dev/null
@@ -1,284 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef MT32EMU_STRUCTURES_H
-#define MT32EMU_STRUCTURES_H
-
-namespace MT32Emu {
-
-const unsigned int MAX_SAMPLE_OUTPUT = 4096;
-
-// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it
-// Roland provides documentation using the sysex-padded addresses, so we tend to use that in code and output
-#define MT32EMU_MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f))
-#define MT32EMU_SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f))
-
-#ifdef _MSC_VER
-#define MT32EMU_ALIGN_PACKED __declspec(align(1))
-typedef unsigned __int64 Bit64u;
-typedef signed __int64 Bit64s;
-#else
-#define MT32EMU_ALIGN_PACKED __attribute__((packed))
-typedef unsigned long long Bit64u;
-typedef signed long long Bit64s;
-#endif
-
-typedef unsigned int Bit32u;
-typedef signed int Bit32s;
-typedef unsigned short int Bit16u;
-typedef signed short int Bit16s;
-typedef unsigned char Bit8u;
-typedef signed char Bit8s;
-
-// The following structures represent the MT-32's memory
-// Since sysex allows this memory to be written to in blocks of bytes,
-// we keep this packed so that we can copy data into the various
-// banks directly
-#if defined(_MSC_VER) || defined (__MINGW32__)
-#pragma pack(push, 1)
-#else
-#pragma pack(1)
-#endif
-
-struct TimbreParam {
- struct commonParam {
- char name[10];
- Bit8u pstruct12; // 1&2 0-12 (1-13)
- Bit8u pstruct34; // #3&4 0-12 (1-13)
- Bit8u pmute; // 0-15 (0000-1111)
- Bit8u nosustain; // 0-1(Normal, No sustain)
- } MT32EMU_ALIGN_PACKED common;
-
- struct partialParam {
- struct wgParam {
- Bit8u coarse; // 0-96 (C1,C#1-C9)
- Bit8u fine; // 0-100 (-50 to +50 (cents?))
- Bit8u keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
- Bit8u bender; // 0,1 (ON/OFF)
- Bit8u waveform; // MT-32: 0-1 (SQU/SAW); LAPC-I: WG WAVEFORM/PCM BANK 0 - 3 (SQU/1, SAW/1, SQU/2, SAW/2)
- Bit8u pcmwave; // 0-127 (1-128)
- Bit8u pulsewid; // 0-100
- Bit8u pwvelo; // 0-14 (-7 - +7)
- } MT32EMU_ALIGN_PACKED wg;
-
- struct envParam {
- Bit8u depth; // 0-10
- Bit8u sensitivity; // 1-100
- Bit8u timekeyfollow; // 0-4
- Bit8u time[4]; // 1-100
- Bit8u level[5]; // 1-100 (-50 - +50)
- } MT32EMU_ALIGN_PACKED env;
-
- struct lfoParam {
- Bit8u rate; // 0-100
- Bit8u depth; // 0-100
- Bit8u modsense; // 0-100
- } MT32EMU_ALIGN_PACKED lfo;
-
- struct tvfParam {
- Bit8u cutoff; // 0-100
- Bit8u resonance; // 0-30
- Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
- Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C)
- Bit8u biaslevel; // 0-14 (-7 - +7)
- Bit8u envdepth; // 0-100
- Bit8u envsense; // 0-100
- Bit8u envdkf; // DEPTH KEY FOLL0W 0-4
- Bit8u envtkf; // TIME KEY FOLLOW 0-4
- Bit8u envtime[5]; // 1-100
- Bit8u envlevel[4]; // 1-100
- } MT32EMU_ALIGN_PACKED tvf;
-
- struct tvaParam {
- Bit8u level; // 0-100
- Bit8u velosens; // 0-100
- Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C)
- Bit8u biaslevel1; // 0-12 (-12 - 0)
- Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C)
- Bit8u biaslevel2; // 0-12 (-12 - 0)
- Bit8u envtkf; // TIME KEY FOLLOW 0-4
- Bit8u envvkf; // VELOS KEY FOLL0W 0-4
- Bit8u envtime[5]; // 1-100
- Bit8u envlevel[4]; // 1-100
- } MT32EMU_ALIGN_PACKED tva;
- } MT32EMU_ALIGN_PACKED partial[4];
-} MT32EMU_ALIGN_PACKED;
-
-struct PatchParam {
- Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
- Bit8u timbreNum; // TIMBRE NUMBER 0-63
- Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones)
- Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents)
- Bit8u benderRange; // BENDER RANGE 0-24
- Bit8u assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
- Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
- Bit8u dummy; // (DUMMY)
-} MT32EMU_ALIGN_PACKED;
-
-struct MemParams {
- // NOTE: The MT-32 documentation only specifies PatchTemp areas for parts 1-8.
- // The LAPC-I documentation specified an additional area for rhythm at the end,
- // where all parameters but fine tune, assign mode and output level are ignored
- struct PatchTemp {
- PatchParam patch;
- Bit8u outlevel; // OUTPUT LEVEL 0-100
- Bit8u panpot; // PANPOT 0-14 (R-L)
- Bit8u dummyv[6];
- } MT32EMU_ALIGN_PACKED;
-
- PatchTemp patchSettings[9];
-
- struct RhythmTemp {
- Bit8u timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF)
- Bit8u outlevel; // OUTPUT LEVEL 0-100
- Bit8u panpot; // PANPOT 0-14 (R-L)
- Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
- } MT32EMU_ALIGN_PACKED;
-
- RhythmTemp rhythmSettings[85];
-
- TimbreParam timbreSettings[8];
-
- PatchParam patches[128];
-
- // NOTE: There are only 30 timbres in the "rhythm" bank for MT-32; the additional 34 are for LAPC-I and above
- struct PaddedTimbre {
- TimbreParam timbre;
- Bit8u padding[10];
- } MT32EMU_ALIGN_PACKED;
-
- PaddedTimbre timbres[64 + 64 + 64 + 64]; // Group A, Group B, Memory, Rhythm
-
- struct SystemArea {
- Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
- Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
- Bit8u reverbTime; // REVERB TIME 0-7 (1-8)
- Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8)
- Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
- Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
- Bit8u masterVol; // MASTER VOLUME 0-100
- } MT32EMU_ALIGN_PACKED;
-
- SystemArea system;
-};
-
-#if defined(_MSC_VER) || defined (__MINGW32__)
-#pragma pack(pop)
-#else
-#pragma pack()
-#endif
-
-struct PCMWaveEntry {
- Bit32u addr;
- Bit32u len;
- double tune;
- bool loop;
-};
-
-struct soundaddr {
- Bit16u pcmplace;
- Bit16u pcmoffset;
-};
-
-struct StereoVolume {
- Bit16s leftvol;
- Bit16s rightvol;
-};
-
-// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings
-struct PatchCache {
- bool playPartial;
- bool PCMPartial;
- int pcm;
- char waveform;
- int pulsewidth;
- int pwsens;
-
- float pitch;
-
- int lfodepth;
- int lforate;
- Bit32u lfoperiod;
- int modsense;
-
- float pitchKeyfollow;
-
- int filtkeyfollow;
-
- int tvfbias;
- int tvfblevel;
- int tvfdir;
-
- int ampbias[2];
- int ampblevel[2];
- int ampdir[2];
-
- int ampdepth;
- int amplevel;
-
- bool useBender;
- float benderRange; // 0.0, 1.0, .., 24.0 (semitones)
-
- TimbreParam::partialParam::envParam pitchEnv;
- TimbreParam::partialParam::tvaParam ampEnv;
- TimbreParam::partialParam::tvfParam filtEnv;
-
- Bit32s pitchsustain;
- Bit32s filtsustain;
-
- Bit32u structureMix;
- int structurePosition;
- int structurePair;
-
- // The following fields are actually common to all partials in the timbre
- bool dirty;
- Bit32u partialCount;
- bool sustain;
- float pitchShift;
- bool reverb;
- const StereoVolume *pansetptr;
-};
-
-class Partial; // Forward reference for class defined in partial.h
-
-struct dpoly {
- bool isPlaying;
-
- unsigned int key;
- int freqnum;
- int vel;
-
- bool isDecay;
-
- const Bit32u *volumeptr;
-
- Partial *partials[4];
-
- bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal
- bool sustain;
-
- bool isActive() const;
- Bit32u getAge() const;
-};
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/synth.cpp b/audio/softsynth/mt32/synth.cpp
deleted file mode 100644
index 8c6c3193a9..0000000000
--- a/audio/softsynth/mt32/synth.cpp
+++ /dev/null
@@ -1,1202 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-// FIXME: Avoid using printf
-#define FORBIDDEN_SYMBOL_EXCEPTION_printf
-
-// FIXME: Avoid using vprintf
-#define FORBIDDEN_SYMBOL_EXCEPTION_vprintf
-
-#include <math.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "mt32emu.h"
-
-#if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__)
-// Older versions of Mac OS X didn't supply a powf function, so using it
-// will cause a binary incompatibility when trying to run a binary built
-// on a newer OS X release on an older one. And Solaris 8 doesn't provide
-// powf, floorf, fabsf etc. at all.
-// Cross-compiled MinGW32 toolchains suffer from a cross-compile bug in
-// libstdc++. math/stubs.o should be empty, but it comes with a symbol for
-// powf, resulting in a linker error because of multiple definitions.
-// Hence we re-define them here. The only potential drawback is that it
-// might be a little bit slower this way.
-#define powf(x,y) ((float)pow(x,y))
-#define floorf(x) ((float)floor(x))
-#define fabsf(x) ((float)fabs(x))
-#endif
-
-namespace MT32Emu {
-
-const int MAX_SYSEX_SIZE = 512;
-
-const ControlROMMap ControlROMMaps[5] = {
- // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrB tmbrBO, tmbrR trC rhythm rhyC rsrv panpot prog
- {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57D0, 0x57E2}, // MT-32 revision 0
- {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57E2, 0x57F4}, // MT-32 revision 0
- {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57BA, 0x57CC}, // MT-32 revision 1
- {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x741C, 85, 0x57E5, 0x57EE, 0x5800}, // MT-32 Blue Ridge mod
- {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, 0x8080, 0x8000, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4F9C, 0x4FAE} // CM-32L
- // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
-};
-
-float iir_filter_normal(float input, float *hist1_ptr, float *coef_ptr) {
- float *hist2_ptr;
- float output,new_hist;
-
- hist2_ptr = hist1_ptr + 1; // next history
-
- // 1st number of coefficients array is overall input scale factor, or filter gain
- output = input * (*coef_ptr++);
-
- output = output - *hist1_ptr * (*coef_ptr++);
- new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
-
- output = new_hist + *hist1_ptr * (*coef_ptr++);
- output = output + *hist2_ptr * (*coef_ptr++); // zeros
-
- *hist2_ptr++ = *hist1_ptr;
- *hist1_ptr++ = new_hist;
- hist1_ptr++;
- hist2_ptr++;
-
- // i = 1
- output = output - *hist1_ptr * (*coef_ptr++);
- new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
-
- output = new_hist + *hist1_ptr * (*coef_ptr++);
- output = output + *hist2_ptr * (*coef_ptr++); // zeros
-
- *hist2_ptr++ = *hist1_ptr;
- *hist1_ptr++ = new_hist;
-
- return(output);
-}
-
-Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
- for (unsigned int i = 0; i < len; i++) {
- checksum = checksum + data[i];
- }
- checksum = checksum & 0x7f;
- if (checksum)
- checksum = 0x80 - checksum;
- return checksum;
-}
-
-Synth::Synth() {
- isOpen = false;
- reverbModel = NULL;
- partialManager = NULL;
- memset(parts, 0, sizeof(parts));
-}
-
-Synth::~Synth() {
- close(); // Make sure we're closed and everything is freed
-}
-
-int Synth::report(ReportType type, const void *data) {
- if (myProp.report != NULL) {
- return myProp.report(myProp.userData, type, data);
- }
- return 0;
-}
-
-void Synth::printDebug(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- if (myProp.printDebug != NULL) {
- myProp.printDebug(myProp.userData, fmt, ap);
- } else {
- vprintf(fmt, ap);
- printf("\n");
- }
- va_end(ap);
-}
-
-void Synth::initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel) {
- // FIXME:KG: I don't think it's necessary to recreate the reverbModel... Just set the parameters
- delete reverbModel;
- reverbModel = new revmodel();
-
- switch (newRevMode) {
- case 0:
- reverbModel->setroomsize(.1f);
- reverbModel->setdamp(.75f);
- break;
- case 1:
- reverbModel->setroomsize(.5f);
- reverbModel->setdamp(.5f);
- break;
- case 2:
- reverbModel->setroomsize(.5f);
- reverbModel->setdamp(.1f);
- break;
- case 3:
- reverbModel->setroomsize(1.0f);
- reverbModel->setdamp(.75f);
- break;
- default:
- reverbModel->setroomsize(.1f);
- reverbModel->setdamp(.5f);
- break;
- }
- reverbModel->setdry(1);
- reverbModel->setwet((float)newRevLevel / 8.0f);
- reverbModel->setwidth((float)newRevTime / 8.0f);
-}
-
-File *Synth::openFile(const char *filename, File::OpenMode mode) {
- // It should never happen that openFile is NULL in our use case.
- // Just to cover the case where something is horrible wrong we
- // use an assert here.
- assert(myProp.openFile != NULL);
- return myProp.openFile(myProp.userData, filename, mode);
-}
-
-void Synth::closeFile(File *file) {
- if (myProp.closeFile != NULL) {
- myProp.closeFile(myProp.userData, file);
- } else {
- file->close();
- delete file;
- }
-}
-
-bool Synth::loadPreset(File *file) {
- bool inSys = false;
- Bit8u sysexBuf[MAX_SYSEX_SIZE];
- Bit16u syslen = 0;
- bool rc = true;
- for (;;) {
- Bit8u c;
- if (!file->readBit8u(&c)) {
- if (!file->isEOF()) {
- rc = false;
- }
- break;
- }
- sysexBuf[syslen] = c;
- if (inSys) {
- syslen++;
- if (c == 0xF7) {
- playSysex(&sysexBuf[0], syslen);
- inSys = false;
- syslen = 0;
- } else if (syslen == MAX_SYSEX_SIZE) {
- printDebug("MAX_SYSEX_SIZE (%d) exceeded while processing preset, ignoring message", MAX_SYSEX_SIZE);
- inSys = false;
- syslen = 0;
- }
- } else if (c == 0xF0) {
- syslen++;
- inSys = true;
- }
- }
- return rc;
-}
-
-bool Synth::loadControlROM(const char *filename) {
- File *file = openFile(filename, File::OpenMode_read); // ROM File
- if (file == NULL) {
- return false;
- }
- bool rc = (file->read(controlROMData, CONTROL_ROM_SIZE) == CONTROL_ROM_SIZE);
-
- closeFile(file);
- if (!rc)
- return rc;
-
- // Control ROM successfully loaded, now check whether it's a known type
- controlROMMap = NULL;
- for (unsigned int i = 0; i < sizeof (ControlROMMaps) / sizeof (ControlROMMaps[0]); i++) {
- if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) {
- controlROMMap = &ControlROMMaps[i];
- return true;
- }
- }
- return false;
-}
-
-bool Synth::loadPCMROM(const char *filename) {
- File *file = openFile(filename, File::OpenMode_read); // ROM File
- if (file == NULL) {
- return false;
- }
- bool rc = true;
- int i;
- for (i = 0; i < pcmROMSize; i++) {
- Bit8u s;
- if (!file->readBit8u(&s)) {
- if (!file->isEOF()) {
- rc = false;
- }
- break;
- }
- Bit8u c;
- if (!file->readBit8u(&c)) {
- if (!file->isEOF()) {
- rc = false;
- } else {
- printDebug("PCM ROM file has an odd number of bytes! Ignoring last");
- }
- break;
- }
-
- short e;
- int bit;
- int u;
- int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
-
- e = 0;
- for (u = 0; u < 15; u++) {
- if (order[u] < 8)
- bit = (s >> (7 - order[u])) & 0x1;
- else
- bit = (c >> (7 - (order[u] - 8))) & 0x1;
- e = e | (short)(bit << (15 - u));
- }
-
- /*
- //Bit16s e = ( ((s & 0x7f) << 4) | ((c & 0x40) << 6) | ((s & 0x80) << 6) | ((c & 0x3f))) << 2;
- if (e<0)
- e = -32767 - e;
- int ut = abs(e);
- int dif = 0x7fff - ut;
- x = exp(((float)((float)0x8000-(float)dif) / (float)0x1000));
- e = (int)((float)e * (x/3200));
- */
-
- // File is companded (dB?), convert to linear PCM
- // MINDB = -96
- // MAXDB = -15
- float testval;
- testval = (float)((~e) & 0x7fff);
- testval = -(testval / 400.00f);
- //testval = -(testval / 341.32291666666666666666666666667);
- float vol = powf(8, testval / 20) * 32767.0f;
-
- if (e > 0)
- vol = -vol;
-
- pcmROMData[i] = (Bit16s)vol;
- }
- if (i != pcmROMSize) {
- printDebug("PCM ROM file is too short (expected %d, got %d)", pcmROMSize, i);
- rc = false;
- }
- closeFile(file);
- return rc;
-}
-
-bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
- ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
- for (int i = 0; i < count; i++) {
- int rAddr = tps[i].pos * 0x800;
- int rLenExp = (tps[i].len & 0x70) >> 4;
- int rLen = 0x800 << rLenExp;
- bool rLoop = (tps[i].len & 0x80) != 0;
- //Bit8u rFlag = tps[i].len & 0x0F;
- Bit16u rTuneOffset = (tps[i].pitchMSB << 8) | tps[i].pitchLSB;
- // The number below is confirmed to a reasonable degree of accuracy on CM-32L
- double STANDARDFREQ = 442.0;
- float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4056.0 - 9.0 / 12.0));
- //printDebug("%f,%d,%d", (double)pTune, tps[i].pitchCoarse, tps[i].pitchFine);
- if (rAddr + rLen > pcmROMSize) {
- printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen);
- return false;
- }
- pcmWaves[i].addr = rAddr;
- pcmWaves[i].len = rLen;
- pcmWaves[i].loop = rLoop;
- pcmWaves[i].tune = rTune;
- }
- return false;
-}
-
-bool Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem, unsigned int memLen) {
- if (memLen < sizeof(TimbreParam::commonParam)) {
- return false;
- }
- TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
- memcpy(&timbre->common, mem, 14);
- unsigned int memPos = 14;
- char drumname[11];
- memset(drumname, 0, 11);
- memcpy(drumname, timbre->common.name, 10);
- for (int t = 0; t < 4; t++) {
- if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
- if (memPos + 58 >= memLen) {
- return false;
- }
- memcpy(&timbre->partial[t], mem + memPos, 58);
- memPos += 58;
- }
- }
- return true;
-}
-
-bool Synth::initRhythmTimbres(Bit16u mapAddress, Bit16u count) {
- const Bit8u *drumMap = &controlROMData[mapAddress];
- int timbreNum = 192;
- for (Bit16u i = 0; i < count * 2; i += 2) {
- Bit16u address = (drumMap[i + 1] << 8) | drumMap[i];
- /*
- // This check is nonsensical when the control ROM is the full 64KB addressable by 16-bit absolute pointers (which it is)
- if (address >= CONTROL_ROM_SIZE) {
- printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
- return false;
- }
- */
- if (!initRhythmTimbre(timbreNum++, &controlROMData[address], CONTROL_ROM_SIZE - address)) {
- printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre 0x%04x", i, address);
- return false;
- }
- }
- return true;
-}
-
-bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre) {
- for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) {
- Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i];
- if (address + sizeof(TimbreParam) > CONTROL_ROM_SIZE) {
- printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
- return false;
- }
- address = address + offset;
- TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre;
- memcpy(timbre, &controlROMData[address], sizeof(TimbreParam));
- }
- return true;
-}
-
-bool Synth::open(SynthProperties &useProp) {
- if (isOpen)
- return false;
-
- myProp = useProp;
- if (useProp.baseDir != NULL) {
- myProp.baseDir = new char[strlen(useProp.baseDir) + 1];
- strcpy(myProp.baseDir, useProp.baseDir);
- }
-
- // This is to help detect bugs
- memset(&mt32ram, '?', sizeof(mt32ram));
-
- printDebug("Loading Control ROM");
- if (!loadControlROM("CM32L_CONTROL.ROM")) {
- if (!loadControlROM("MT32_CONTROL.ROM")) {
- printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM");
- report(ReportType_errorControlROM, NULL);
- return false;
- }
- }
-
- // 512KB PCM ROM for MT-32, etc.
- // 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500
- // Note that the size below is given in samples (16-bit), not bytes
- pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024;
- pcmROMData = new Bit16s[pcmROMSize];
-
- printDebug("Loading PCM ROM");
- if (!loadPCMROM("CM32L_PCM.ROM")) {
- if (!loadPCMROM("MT32_PCM.ROM")) {
- printDebug("Init Error - Missing MT32_PCM.ROM");
- report(ReportType_errorPCMROM, NULL);
- return false;
- }
- }
-
- printDebug("Initializing Timbre Bank A");
- if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0)) {
- return false;
- }
-
- printDebug("Initializing Timbre Bank B");
- if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 64)) {
- return false;
- }
-
- printDebug("Initializing Timbre Bank R");
- if (!initRhythmTimbres(controlROMMap->timbreRMap, controlROMMap->timbreRCount)) {
- return false;
- }
-
- printDebug("Initializing Timbre Bank M");
- // CM-64 seems to initialize all bytes in this bank to 0.
- memset(&mt32ram.timbres[128], 0, sizeof (mt32ram.timbres[128]) * 64);
-
- partialManager = new PartialManager(this);
-
- pcmWaves = new PCMWaveEntry[controlROMMap->pcmCount];
-
- printDebug("Initializing PCM List");
- initPCMList(controlROMMap->pcmTable, controlROMMap->pcmCount);
-
- printDebug("Initializing Rhythm Temp");
- memcpy(mt32ram.rhythmSettings, &controlROMData[controlROMMap->rhythmSettings], controlROMMap->rhythmSettingsCount * 4);
-
- printDebug("Initializing Patches");
- for (Bit8u i = 0; i < 128; i++) {
- PatchParam *patch = &mt32ram.patches[i];
- patch->timbreGroup = i / 64;
- patch->timbreNum = i % 64;
- patch->keyShift = 24;
- patch->fineTune = 50;
- patch->benderRange = 12;
- patch->assignMode = 0;
- patch->reverbSwitch = 1;
- patch->dummy = 0;
- }
-
- printDebug("Initializing System");
- // The MT-32 manual claims that "Standard pitch" is 442Hz.
- mt32ram.system.masterTune = 0x40; // Confirmed on CM-64 as 0x4A, but SCUMM games use 0x40 and we don't want to initialize twice
- mt32ram.system.reverbMode = 0; // Confirmed
- mt32ram.system.reverbTime = 5; // Confirmed
- mt32ram.system.reverbLevel = 3; // Confirmed
- memcpy(mt32ram.system.reserveSettings, &controlROMData[controlROMMap->reserveSettings], 9); // Confirmed
- for (Bit8u i = 0; i < 9; i++) {
- // This is the default: {1, 2, 3, 4, 5, 6, 7, 8, 9}
- // An alternative configuration can be selected by holding "Master Volume"
- // and pressing "PART button 1" on the real MT-32's frontpanel.
- // The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9}
- mt32ram.system.chanAssign[i] = i + 1;
- }
- mt32ram.system.masterVol = 100; // Confirmed
- if (!refreshSystem())
- return false;
-
- for (int i = 0; i < 8; i++) {
- mt32ram.patchSettings[i].outlevel = 80;
- mt32ram.patchSettings[i].panpot = controlROMData[controlROMMap->panSettings + i];
- memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv));
- parts[i] = new Part(this, i);
- parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
- }
- parts[8] = new RhythmPart(this, 8);
-
- // For resetting mt32 mid-execution
- mt32default = mt32ram;
-
- iirFilter = &iir_filter_normal;
-
-#ifdef MT32EMU_HAVE_X86
- bool availableSSE = DetectSIMD();
- bool available3DNow = Detect3DNow();
-
- if (availableSSE)
- report(ReportType_availableSSE, NULL);
- if (available3DNow)
- report(ReportType_available3DNow, NULL);
-
- if (available3DNow) {
- printDebug("Detected and using SIMD (AMD 3DNow) extensions");
- iirFilter = &iir_filter_3dnow;
- report(ReportType_using3DNow, NULL);
- } else if (availableSSE) {
- printDebug("Detected and using SIMD (Intel SSE) extensions");
- iirFilter = &iir_filter_sse;
- report(ReportType_usingSSE, NULL);
- }
-#endif
-
- isOpen = true;
- isEnabled = false;
-
- printDebug("*** Initialisation complete ***");
- return true;
-}
-
-void Synth::close(void) {
- if (!isOpen)
- return;
-
- tables.freeNotes();
- if (partialManager != NULL) {
- delete partialManager;
- partialManager = NULL;
- }
-
- if (reverbModel != NULL) {
- delete reverbModel;
- reverbModel = NULL;
- }
-
- for (int i = 0; i < 9; i++) {
- if (parts[i] != NULL) {
- delete parts[i];
- parts[i] = NULL;
- }
- }
- if (myProp.baseDir != NULL) {
- delete myProp.baseDir;
- myProp.baseDir = NULL;
- }
-
- delete[] pcmWaves;
- delete[] pcmROMData;
- isOpen = false;
-}
-
-void Synth::playMsg(Bit32u msg) {
- // FIXME: Implement active sensing
- unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
- unsigned char chan = (unsigned char) (msg & 0x00000F);
- unsigned char note = (unsigned char)((msg & 0x00FF00) >> 8);
- unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16);
- isEnabled = true;
-
- //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
-
- signed char part = chantable[chan];
- if (part < 0 || part > 8) {
- printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity);
- return;
- }
- playMsgOnPart(part, code, note, velocity);
-}
-
-void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
- Bit32u bend;
-
- //printDebug("Synth::playMsg(0x%02x)",msg);
- switch (code) {
- case 0x8:
- //printDebug("Note OFF - Part %d", part);
- // The MT-32 ignores velocity for note off
- parts[part]->stopNote(note);
- break;
- case 0x9:
- //printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity);
- if (velocity == 0) {
- // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40
- parts[part]->stopNote(note);
- } else {
- parts[part]->playNote(note, velocity);
- }
- break;
- case 0xB: // Control change
- switch (note) {
- case 0x01: // Modulation
- //printDebug("Modulation: %d", velocity);
- parts[part]->setModulation(velocity);
- break;
- case 0x07: // Set volume
- //printDebug("Volume set: %d", velocity);
- parts[part]->setVolume(velocity);
- break;
- case 0x0A: // Pan
- //printDebug("Pan set: %d", velocity);
- parts[part]->setPan(velocity);
- break;
- case 0x0B:
- //printDebug("Expression set: %d", velocity);
- parts[part]->setExpression(velocity);
- break;
- case 0x40: // Hold (sustain) pedal
- //printDebug("Hold pedal set: %d", velocity);
- parts[part]->setHoldPedal(velocity>=64);
- break;
-
- case 0x79: // Reset all controllers
- //printDebug("Reset all controllers");
- //FIXME: Check for accuracy against real thing
- parts[part]->setVolume(100);
- parts[part]->setExpression(127);
- parts[part]->setPan(64);
- parts[part]->setBend(0x2000);
- parts[part]->setHoldPedal(false);
- break;
-
- case 0x7B: // All notes off
- //printDebug("All notes off");
- parts[part]->allNotesOff();
- break;
-
- default:
- printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity);
- break;
- }
-
- break;
- case 0xC: // Program change
- //printDebug("Program change %01x", note);
- parts[part]->setProgram(note);
- break;
- case 0xE: // Pitch bender
- bend = (velocity << 7) | (note);
- //printDebug("Pitch bender %02x", bend);
- parts[part]->setBend(bend);
- break;
- default:
- printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
- break;
- }
-
- //midiOutShortMsg(m_out, msg);
-}
-
-void Synth::playSysex(const Bit8u *sysex, Bit32u len) {
- if (len < 2) {
- printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
- }
- if (sysex[0] != 0xF0) {
- printDebug("playSysex: Message lacks start-of-sysex (0xF0)");
- return;
- }
- // Due to some programs (e.g. Java) sending buffers with junk at the end, we have to go through and find the end marker rather than relying on len.
- Bit32u endPos;
- for (endPos = 1; endPos < len; endPos++)
- {
- if (sysex[endPos] == 0xF7)
- break;
- }
- if (endPos == len) {
- printDebug("playSysex: Message lacks end-of-sysex (0xf7)");
- return;
- }
- playSysexWithoutFraming(sysex + 1, endPos - 1);
-}
-
-void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) {
- if (len < 4) {
- printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
- return;
- }
- if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) {
- printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
- return;
- }
- if (sysex[2] == SYSEX_MDL_D50) {
- printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
- return;
- }
- else if (sysex[2] != SYSEX_MDL_MT32) {
- printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
- return;
- }
- playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4);
-}
-
-void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) {
- if (device > 0x10) {
- // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels
- printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device);
- return;
- }
- if (len < 4) {
- printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
- return;
- }
- unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
- if (checksum != sysex[len - 1]) {
- printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
- return;
- }
- len -= 1; // Exclude checksum
- switch (command) {
- case SYSEX_CMD_DT1:
- writeSysex(device, sysex, len);
- break;
- case SYSEX_CMD_RQ1:
- readSysex(device, sysex, len);
- break;
- default:
- printDebug("playSysexWithoutFraming: Unsupported command %02x", command);
- return;
- }
-}
-
-void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) {
-}
-
-const MemoryRegion memoryRegions[8] = {
- {MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9},
- {MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85},
- {MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8},
- {MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128},
- {MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64},
- {MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::SystemArea), 1},
- {MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1},
- {MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1}
-};
-
-const int NUM_REGIONS = sizeof(memoryRegions) / sizeof(MemoryRegion);
-
-void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
- Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
- addr = MT32EMU_MEMADDR(addr);
- sysex += 3;
- len -= 3;
- //printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr));
- // NOTE: Please keep both lower and upper bounds in each check, for ease of reading
-
- // Process channel-specific sysex by converting it to device-global
- if (device < 0x10) {
- printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
- if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
- int offset;
- if (chantable[device] == -1) {
- printDebug(" (Channel not mapped to a partial... 0 offset)");
- offset = 0;
- } else if (chantable[device] == 8) {
- printDebug(" (Channel mapped to rhythm... 0 offset)");
- offset = 0;
- } else {
- offset = chantable[device] * sizeof(MemParams::PatchTemp);
- printDebug(" (Setting extra offset to %d)", offset);
- }
- addr += MT32EMU_MEMADDR(0x030000) + offset;
- } else if (/*addr >= 0x010000 && */ addr < MT32EMU_MEMADDR(0x020000)) {
- addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
- } else if (/*addr >= 0x020000 && */ addr < MT32EMU_MEMADDR(0x030000)) {
- int offset;
- if (chantable[device] == -1) {
- printDebug(" (Channel not mapped to a partial... 0 offset)");
- offset = 0;
- } else if (chantable[device] == 8) {
- printDebug(" (Channel mapped to rhythm... 0 offset)");
- offset = 0;
- } else {
- offset = chantable[device] * sizeof(TimbreParam);
- printDebug(" (Setting extra offset to %d)", offset);
- }
- addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset;
- } else {
- printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
- return;
- }
- }
-
- // Process device-global sysex (possibly converted from channel-specific sysex above)
- for (;;) {
- // Find the appropriate memory region
- int regionNum;
- const MemoryRegion *region = NULL; // Initialized to please compiler
- for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
- region = &memoryRegions[regionNum];
- if (region->contains(addr)) {
- writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex);
- break;
- }
- }
- if (regionNum == NUM_REGIONS) {
- printDebug("Sysex write to unrecognized address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len);
- break;
- }
- Bit32u next = region->next(addr, len);
- if (next == 0) {
- break;
- }
- addr += next;
- sysex += next;
- len -= next;
- }
-}
-
-void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) {
- int regionNum;
- const MemoryRegion *region = NULL;
- for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
- region = &memoryRegions[regionNum];
- if (region->contains(addr)) {
- readMemoryRegion(region, addr, len, data);
- break;
- }
- }
-}
-
-void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data) {
- unsigned int first = region->firstTouched(addr);
- //unsigned int last = region->lastTouched(addr, len);
- unsigned int off = region->firstTouchedOffset(addr);
- len = region->getClampedLen(addr, len);
-
- unsigned int m;
-
- switch (region->type) {
- case MR_PatchTemp:
- for (m = 0; m < len; m++)
- data[m] = ((Bit8u *)&mt32ram.patchSettings[first])[off + m];
- break;
- case MR_RhythmTemp:
- for (m = 0; m < len; m++)
- data[m] = ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m];
- break;
- case MR_TimbreTemp:
- for (m = 0; m < len; m++)
- data[m] = ((Bit8u *)&mt32ram.timbreSettings[first])[off + m];
- break;
- case MR_Patches:
- for (m = 0; m < len; m++)
- data[m] = ((Bit8u *)&mt32ram.patches[first])[off + m];
- break;
- case MR_Timbres:
- for (m = 0; m < len; m++)
- data[m] = ((Bit8u *)&mt32ram.timbres[first])[off + m];
- break;
- case MR_System:
- for (m = 0; m < len; m++)
- data[m] = ((Bit8u *)&mt32ram.system)[m + off];
- break;
- default:
- for (m = 0; m < len; m += 2) {
- data[m] = 0xff;
- if (m + 1 < len) {
- data[m+1] = (Bit8u)region->type;
- }
- }
- // TODO: Don't care about the others ATM
- break;
- }
-
-}
-
-void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data) {
- unsigned int first = region->firstTouched(addr);
- unsigned int last = region->lastTouched(addr, len);
- unsigned int off = region->firstTouchedOffset(addr);
- switch (region->type) {
- case MR_PatchTemp:
- for (unsigned int m = 0; m < len; m++) {
- ((Bit8u *)&mt32ram.patchSettings[first])[off + m] = data[m];
- }
- //printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);
-
- for (unsigned int i = first; i <= last; i++) {
- int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum;
- char timbreName[11];
- memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10);
- timbreName[10] = 0;
- printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s), outlevel=%d", first, last, off, off + len, i, absTimbreNum, timbreName, mt32ram.patchSettings[i].outlevel);
- if (parts[i] != NULL) {
- if (i != 8) {
- // Note: Confirmed on CM-64 that we definitely *should* update the timbre here,
- // but only in the case that the sysex actually writes to those values
- if (i == first && off > 2) {
- printDebug(" (Not updating timbre, since those values weren't touched)");
- } else {
- parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
- }
- }
- parts[i]->refresh();
- }
- }
- break;
- case MR_RhythmTemp:
- for (unsigned int m = 0; m < len; m++)
- ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m] = data[m];
- for (unsigned int i = first; i <= last; i++) {
- int timbreNum = mt32ram.rhythmSettings[i].timbre;
- char timbreName[11];
- if (timbreNum < 94) {
- memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10);
- timbreName[10] = 0;
- } else {
- strcpy(timbreName, "[None]");
- }
- printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", first, last, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName);
- }
- if (parts[8] != NULL) {
- parts[8]->refresh();
- }
- break;
- case MR_TimbreTemp:
- for (unsigned int m = 0; m < len; m++)
- ((Bit8u *)&mt32ram.timbreSettings[first])[off + m] = data[m];
- for (unsigned int i = first; i <= last; i++) {
- char instrumentName[11];
- memcpy(instrumentName, mt32ram.timbreSettings[i].common.name, 10);
- instrumentName[10] = 0;
- printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", first, last, off, off + len, i, instrumentName);
- if (parts[i] != NULL) {
- parts[i]->refresh();
- }
- }
- break;
- case MR_Patches:
- for (unsigned int m = 0; m < len; m++)
- ((Bit8u *)&mt32ram.patches[first])[off + m] = data[m];
- for (unsigned int i = first; i <= last; i++) {
- PatchParam *patch = &mt32ram.patches[i];
- int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;
- char instrumentName[11];
- memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10);
- instrumentName[10] = 0;
- Bit8u *n = (Bit8u *)patch;
- printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", first, last, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]);
- // FIXME:KG: The below is definitely dodgy. We just guess that this is the patch that the part was using
- // based on a timbre match (but many patches could have the same timbre!)
- // If this refresh is really correct, we should store the patch number in use by each part.
- /*
- for (int part = 0; part < 8; part++) {
- if (parts[part] != NULL) {
- int partPatchAbsTimbreNum = mt32ram.patchSettings[part].patch.timbreGroup * 64 + mt32ram.patchSettings[part].patch.timbreNum;
- if (parts[part]->getAbsTimbreNum() == patchAbsTimbreNum) {
- parts[part]->setPatch(patch);
- parts[part]->RefreshPatch();
- }
- }
- }
- */
- }
- break;
- case MR_Timbres:
- // Timbres
- first += 128;
- last += 128;
- for (unsigned int m = 0; m < len; m++)
- ((Bit8u *)&mt32ram.timbres[first])[off + m] = data[m];
- for (unsigned int i = first; i <= last; i++) {
- char instrumentName[11];
- memcpy(instrumentName, mt32ram.timbres[i].timbre.common.name, 10);
- instrumentName[10] = 0;
- printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", first, last, off, off + len, i, instrumentName);
- // FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)...
- // Does the real MT-32 automatically do this?
- for (unsigned int part = 0; part < 9; part++) {
- if (parts[part] != NULL) {
- parts[part]->refreshTimbre(i);
- }
- }
- }
- break;
- case MR_System:
- for (unsigned int m = 0; m < len; m++)
- ((Bit8u *)&mt32ram.system)[m + off] = data[m];
-
- report(ReportType_devReconfig, NULL);
-
- printDebug("WRITE-SYSTEM:");
- refreshSystem();
- break;
- case MR_Display:
- char buf[MAX_SYSEX_SIZE];
- memcpy(&buf, &data[0], len);
- buf[len] = 0;
- printDebug("WRITE-LCD: %s", buf);
- report(ReportType_lcdMessage, buf);
- break;
- case MR_Reset:
- printDebug("RESET");
- report(ReportType_devReset, NULL);
- partialManager->deactivateAll();
- mt32ram = mt32default;
- for (int i = 0; i < 9; i++) {
- parts[i]->refresh();
- }
- isEnabled = false;
- break;
- }
-}
-
-bool Synth::refreshSystem() {
- memset(chantable, -1, sizeof(chantable));
-
- for (unsigned int i = 0; i < 9; i++) {
- //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.system.chanAssign[i]);
- if (mt32ram.system.chanAssign[i] == 16 && parts[i] != NULL) {
- parts[i]->allSoundOff();
- } else {
- chantable[(int)mt32ram.system.chanAssign[i]] = (char)i;
- }
- }
- //FIXME:KG: This is just an educated guess.
- // The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here)
- // The MT-32 documentation claims a range of 432.1Hz-457.6Hz
- masterTune = 440.0f * powf(2.0f, (mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f));
- printDebug(" Master Tune: %f", (double)masterTune);
- printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
- report(ReportType_newReverbMode, &mt32ram.system.reverbMode);
- report(ReportType_newReverbTime, &mt32ram.system.reverbTime);
- report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel);
-
- if (myProp.useDefaultReverb) {
- initReverb(mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
- } else {
- initReverb(myProp.reverbType, myProp.reverbTime, mt32ram.system.reverbLevel);
- }
-
- Bit8u *rset = mt32ram.system.reserveSettings;
- printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
- int pr = partialManager->setReserve(rset);
- if (pr != 32)
- printDebug(" (Partial Reserve Table with less than 32 partials reserved!)");
- rset = mt32ram.system.chanAssign;
- printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
- printDebug(" Master volume: %d", mt32ram.system.masterVol);
- masterVolume = (Bit16u)(mt32ram.system.masterVol * 32767 / 100);
- if (!tables.init(this, pcmWaves, (float)myProp.sampleRate, masterTune)) {
- report(ReportType_errorSampleRate, NULL);
- return false;
- }
- return true;
-}
-
-bool Synth::dumpTimbre(File *file, const TimbreParam *timbre, Bit32u address) {
- // Sysex header
- if (!file->writeBit8u(0xF0))
- return false;
- if (!file->writeBit8u(0x41))
- return false;
- if (!file->writeBit8u(0x10))
- return false;
- if (!file->writeBit8u(0x16))
- return false;
- if (!file->writeBit8u(0x12))
- return false;
-
- char lsb = (char)(address & 0x7f);
- char isb = (char)((address >> 7) & 0x7f);
- char msb = (char)(((address >> 14) & 0x7f) | 0x08);
-
- //Address
- if (!file->writeBit8u(msb))
- return false;
- if (!file->writeBit8u(isb))
- return false;
- if (!file->writeBit8u(lsb))
- return false;
-
- //Data
- if (file->write(timbre, 246) != 246)
- return false;
-
- //Checksum
- unsigned char checksum = calcSysexChecksum((const Bit8u *)timbre, 246, msb + isb + lsb);
- if (!file->writeBit8u(checksum))
- return false;
-
- //End of sysex
- if (!file->writeBit8u(0xF7))
- return false;
- return true;
-}
-
-int Synth::dumpTimbres(const char *filename, int start, int len) {
- File *file = openFile(filename, File::OpenMode_write);
- if (file == NULL)
- return -1;
-
- for (int timbreNum = start; timbreNum < start + len; timbreNum++) {
- int useaddr = (timbreNum - start) * 256;
- TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
- if (!dumpTimbre(file, timbre, useaddr))
- break;
- }
- closeFile(file);
- return 0;
-}
-
-void ProduceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
-#if MT32EMU_USE_MMX > 2
- //FIXME:KG: This appears to introduce crackle
- int donelen = i386_produceOutput1(useBuf, stream, len, volume);
- len -= donelen;
- stream += donelen * 2;
- useBuf += donelen * 2;
-#endif
- int end = len * 2;
- while (end--) {
- *stream = *stream + (Bit16s)(((Bit32s)*useBuf++ * (Bit32s)volume)>>15);
- stream++;
- }
-}
-
-void Synth::render(Bit16s *stream, Bit32u len) {
- memset(stream, 0, len * sizeof (Bit16s) * 2);
- if (!isEnabled)
- return;
- while (len > 0) {
- Bit32u thisLen = len > MAX_SAMPLE_OUTPUT ? MAX_SAMPLE_OUTPUT : len;
- doRender(stream, thisLen);
- len -= thisLen;
- stream += 2 * thisLen;
- }
-}
-
-void Synth::doRender(Bit16s *stream, Bit32u len) {
- partialManager->ageAll();
-
- if (myProp.useReverb) {
- for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialManager->shouldReverb(i)) {
- if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
- ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
- }
- }
- }
- Bit32u m = 0;
- for (unsigned int i = 0; i < len; i++) {
- sndbufl[i] = (float)stream[m] / 32767.0f;
- m++;
- sndbufr[i] = (float)stream[m] / 32767.0f;
- m++;
- }
- reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
- m=0;
- for (unsigned int i = 0; i < len; i++) {
- stream[m] = (Bit16s)(outbufl[i] * 32767.0f);
- m++;
- stream[m] = (Bit16s)(outbufr[i] * 32767.0f);
- m++;
- }
- for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (!partialManager->shouldReverb(i)) {
- if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
- ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
- }
- }
- }
- } else {
- for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialManager->produceOutput(i, &tmpBuffer[0], len))
- ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
- }
- }
-
- partialManager->clearAlreadyOutputed();
-
-#if MT32EMU_MONITOR_PARTIALS == 1
- samplepos += len;
- if (samplepos > myProp.SampleRate * 5) {
- samplepos = 0;
- int partialUsage[9];
- partialManager->GetPerPartPartialUsage(partialUsage);
- printDebug("1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7]);
- printDebug("Rhythm: %02d TOTAL: %02d", partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->GetFreePartialCount());
- }
-#endif
-}
-
-const Partial *Synth::getPartial(unsigned int partialNum) const {
- return partialManager->getPartial(partialNum);
-}
-
-const Part *Synth::getPart(unsigned int partNum) const {
- if (partNum > 8)
- return NULL;
- return parts[partNum];
-}
-
-}
diff --git a/audio/softsynth/mt32/synth.h b/audio/softsynth/mt32/synth.h
deleted file mode 100644
index 0ef2c9d135..0000000000
--- a/audio/softsynth/mt32/synth.h
+++ /dev/null
@@ -1,299 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef MT32EMU_SYNTH_H
-#define MT32EMU_SYNTH_H
-
-#include "common/scummsys.h"
-
-class revmodel;
-
-namespace MT32Emu {
-
-class File;
-class Partial;
-class PartialManager;
-class Part;
-
-enum ReportType {
- // Errors
- ReportType_errorControlROM = 1,
- ReportType_errorPCMROM,
- ReportType_errorSampleRate,
-
- // Progress
- ReportType_progressInit,
-
- // HW spec
- ReportType_availableSSE,
- ReportType_available3DNow,
- ReportType_usingSSE,
- ReportType_using3DNow,
-
- // General info
- ReportType_lcdMessage,
- ReportType_devReset,
- ReportType_devReconfig,
- ReportType_newReverbMode,
- ReportType_newReverbTime,
- ReportType_newReverbLevel
-};
-
-struct SynthProperties {
- // Sample rate to use in mixing
- int sampleRate;
-
- // Flag to activate reverb. True = use reverb, False = no reverb
- bool useReverb;
- // True to use software set reverb settings, False to set reverb settings in
- // following parameters
- bool useDefaultReverb;
- // When not using the default settings, this specifies one of the 4 reverb types
- // 1 = Room 2 = Hall 3 = Plate 4 = Tap
- unsigned char reverbType;
- // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
- unsigned char reverbTime;
- // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
- unsigned char reverbLevel;
- // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash)
- // Not used if "openFile" is set. May be NULL in any case.
- char *baseDir;
- // This is used as the first argument to all callbacks
- void *userData;
- // Callback for reporting various errors and information. May be NULL
- int (*report)(void *userData, ReportType type, const void *reportData);
- // Callback for debug messages, in vprintf() format
- void (*printDebug)(void *userData, const char *fmt, va_list list);
- // Callback for providing an implementation of File, opened and ready for use
- // May be NULL, in which case a default implementation will be used.
- File *(*openFile)(void *userData, const char *filename, File::OpenMode mode);
- // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted.
- void (*closeFile)(void *userData, File *file);
-};
-
-// This is the specification of the Callback routine used when calling the RecalcWaveforms
-// function
-typedef void (*recalcStatusCallback)(int percDone);
-
-// This external function recreates the base waveform file (waveforms.raw) using a specifed
-// sampling rate. The callback routine provides interactivity to let the user know what
-// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the
-// callback routine, no status is reported.
-bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
-
-typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr);
-
-const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
-
-const Bit8u SYSEX_MDL_MT32 = 0x16;
-const Bit8u SYSEX_MDL_D50 = 0x14;
-
-const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1
-const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1
-const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data
-const Bit8u SYSEX_CMD_RQD = 0x41; // Request data
-const Bit8u SYSEX_CMD_DAT = 0x42; // Data set
-const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge
-const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
-const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
-const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
-
-const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
-
-struct ControlROMPCMStruct
-{
- Bit8u pos;
- Bit8u len;
- Bit8u pitchLSB;
- Bit8u pitchMSB;
-};
-
-struct ControlROMMap {
- Bit16u idPos;
- Bit16u idLen;
- const char *idBytes;
- Bit16u pcmTable;
- Bit16u pcmCount;
- Bit16u timbreAMap;
- Bit16u timbreAOffset;
- Bit16u timbreBMap;
- Bit16u timbreBOffset;
- Bit16u timbreRMap;
- Bit16u timbreRCount;
- Bit16u rhythmSettings;
- Bit16u rhythmSettingsCount;
- Bit16u reserveSettings;
- Bit16u panSettings;
- Bit16u programSettings;
-};
-
-enum MemoryRegionType {
- MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
-};
-
-class MemoryRegion {
-public:
- MemoryRegionType type;
- Bit32u startAddr, entrySize, entries;
-
- int lastTouched(Bit32u addr, Bit32u len) const {
- return (offset(addr) + len - 1) / entrySize;
- }
- int firstTouchedOffset(Bit32u addr) const {
- return offset(addr) % entrySize;
- }
- int firstTouched(Bit32u addr) const {
- return offset(addr) / entrySize;
- }
- Bit32u regionEnd() const {
- return startAddr + entrySize * entries;
- }
- bool contains(Bit32u addr) const {
- return addr >= startAddr && addr < regionEnd();
- }
- int offset(Bit32u addr) const {
- return addr - startAddr;
- }
- Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
- if (addr + len > regionEnd())
- return regionEnd() - addr;
- return len;
- }
- Bit32u next(Bit32u addr, Bit32u len) const {
- if (addr + len > regionEnd()) {
- return regionEnd() - addr;
- }
- return 0;
- }
-};
-
-
-class Synth {
-friend class Part;
-friend class RhythmPart;
-friend class Partial;
-friend class Tables;
-private:
- bool isEnabled;
-
- iir_filter_type iirFilter;
-
- PCMWaveEntry *pcmWaves; // Array
-
- const ControlROMMap *controlROMMap;
- Bit8u controlROMData[CONTROL_ROM_SIZE];
- Bit16s *pcmROMData;
- int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
-
- Bit8s chantable[32];
-
- #if MT32EMU_MONITOR_PARTIALS == 1
- static Bit32s samplepos = 0;
- #endif
-
- Tables tables;
-
- MemParams mt32ram, mt32default;
-
- revmodel *reverbModel;
-
- float masterTune;
- Bit16u masterVolume;
-
- bool isOpen;
-
- PartialManager *partialManager;
- Part *parts[9];
-
- Bit16s tmpBuffer[MAX_SAMPLE_OUTPUT * 2];
- float sndbufl[MAX_SAMPLE_OUTPUT];
- float sndbufr[MAX_SAMPLE_OUTPUT];
- float outbufl[MAX_SAMPLE_OUTPUT];
- float outbufr[MAX_SAMPLE_OUTPUT];
-
- SynthProperties myProp;
-
- bool loadPreset(File *file);
- void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel);
- void doRender(Bit16s * stream, Bit32u len);
-
- void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
- void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
- void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data);
- void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data);
-
- bool loadControlROM(const char *filename);
- bool loadPCMROM(const char *filename);
- bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr);
- int dumpTimbres(const char *filename, int start, int len);
-
- bool initPCMList(Bit16u mapAddress, Bit16u count);
- bool initRhythmTimbres(Bit16u mapAddress, Bit16u count);
- bool initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre);
- bool initRhythmTimbre(int drumNum, const Bit8u *mem, unsigned int memLen);
- bool refreshSystem();
-
-protected:
- int report(ReportType type, const void *reportData);
- File *openFile(const char *filename, File::OpenMode mode);
- void closeFile(File *file);
- void printDebug(const char *fmt, ...) GCC_PRINTF(2, 3);
-
-public:
- static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
-
- Synth();
- ~Synth();
-
- // Used to initialize the MT-32. Must be called before any other function.
- // Returns true if initialization was sucessful, otherwise returns false.
- bool open(SynthProperties &useProp);
-
- // Closes the MT-32 and deallocates any memory used by the synthesizer
- void close(void);
-
- // Sends a 4-byte MIDI message to the MT-32 for immediate playback
- void playMsg(Bit32u msg);
- void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
-
- // Sends a string of Sysex commands to the MT-32 for immediate interpretation
- // The length is in bytes
- void playSysex(const Bit8u *sysex, Bit32u len);
- void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
- void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len);
- void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
-
- // This callback routine is used to have the MT-32 generate samples to the specified
- // output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo,
- // one sample is 4 bytes)
- void render(Bit16s * stream, Bit32u len);
-
- const Partial *getPartial(unsigned int partialNum) const;
-
- void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
-
- // partNum should be 0..7 for Part 1..8, or 8 for Rhythm
- const Part *getPart(unsigned int partNum) const;
-};
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/tables.cpp b/audio/softsynth/mt32/tables.cpp
deleted file mode 100644
index 9fdb595467..0000000000
--- a/audio/softsynth/mt32/tables.cpp
+++ /dev/null
@@ -1,761 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-
-// FIXME: Avoid using rand
-#define FORBIDDEN_SYMBOL_EXCEPTION_rand
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "mt32emu.h"
-
-#if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__)
-// Older versions of Mac OS X didn't supply a powf function, so using it
-// will cause a binary incompatibility when trying to run a binary built
-// on a newer OS X release on an older one. And Solaris 8 doesn't provide
-// powf, floorf, fabsf etc. at all.
-// Cross-compiled MinGW32 toolchains suffer from a cross-compile bug in
-// libstdc++. math/stubs.o should be empty, but it comes with a symbol for
-// powf, resulting in a linker error because of multiple definitions.
-// Hence we re-define them here. The only potential drawback is that it
-// might be a little bit slower this way.
-#define powf(x,y) ((float)pow(x,y))
-#define floorf(x) ((float)floor(x))
-#define fabsf(x) ((float)fabs(x))
-#endif
-
-#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))
-
-namespace MT32Emu {
-
-//Amplitude time velocity follow exponential coefficients
-static const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
-static const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};
-
-// These are division constants for the TVF depth key follow
-static const Bit32u depexp[5] = {3000, 950, 485, 255, 138};
-
-//Envelope time keyfollow exponential coefficients
-static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215};
-static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067};
-
-// Begin filter stuff
-
-// Pre-warp the coefficients of a numerator or denominator.
-// Note that a0 is assumed to be 1, so there is no wrapping
-// of it.
-static void prewarp(double *a1, double *a2, double fc, double fs) {
- double wp;
-
- wp = 2.0 * fs * tan(DOUBLE_PI * fc / fs);
-
- *a2 = *a2 / (wp * wp);
- *a1 = *a1 / wp;
-}
-
-// Transform the numerator and denominator coefficients
-// of s-domain biquad section into corresponding
-// z-domain coefficients.
-//
-// Store the 4 IIR coefficients in array pointed by coef
-// in following order:
-// beta1, beta2 (denominator)
-// alpha1, alpha2 (numerator)
-//
-// Arguments:
-// a0-a2 - s-domain numerator coefficients
-// b0-b2 - s-domain denominator coefficients
-// k - filter gain factor. initially set to 1
-// and modified by each biquad section in such
-// a way, as to make it the coefficient by
-// which to multiply the overall filter gain
-// in order to achieve a desired overall filter gain,
-// specified in initial value of k.
-// fs - sampling rate (Hz)
-// coef - array of z-domain coefficients to be filled in.
-//
-// Return:
-// On return, set coef z-domain coefficients
-static void bilinear(double a0, double a1, double a2, double b0, double b1, double b2, double *k, double fs, float *coef) {
- double ad, bd;
-
- // alpha (Numerator in s-domain)
- ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0;
- // beta (Denominator in s-domain)
- bd = 4. * b2 * fs * fs + 2. * b1* fs + b0;
-
- // update gain constant for this section
- *k *= ad/bd;
-
- // Denominator
- *coef++ = (float)((2. * b0 - 8. * b2 * fs * fs) / bd); // beta1
- *coef++ = (float)((4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd); // beta2
-
- // Nominator
- *coef++ = (float)((2. * a0 - 8. * a2 * fs * fs) / ad); // alpha1
- *coef = (float)((4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad); // alpha2
-}
-
-// a0-a2: numerator coefficients
-// b0-b2: denominator coefficients
-// fc: Filter cutoff frequency
-// fs: sampling rate
-// k: overall gain factor
-// coef: pointer to 4 iir coefficients
-static void szxform(double *a0, double *a1, double *a2, double *b0, double *b1, double *b2, double fc, double fs, double *k, float *coef) {
- // Calculate a1 and a2 and overwrite the original values
- prewarp(a1, a2, fc, fs);
- prewarp(b1, b2, fc, fs);
- bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef);
-}
-
-static void initFilter(float fs, float fc, float *icoeff, float Q) {
- float *coef;
- double a0, a1, a2, b0, b1, b2;
-
- double k = 1.5; // Set overall filter gain factor
- coef = icoeff + 1; // Skip k, or gain
-
- // Section 1
- a0 = 1.0;
- a1 = 0;
- a2 = 0;
- b0 = 1.0;
- b1 = 0.765367 / Q; // Divide by resonance or Q
- b2 = 1.0;
- szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
- coef += 4; // Point to next filter section
-
- // Section 2
- a0 = 1.0;
- a1 = 0;
- a2 = 0;
- b0 = 1.0;
- b1 = 1.847759 / Q;
- b2 = 1.0;
- szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
-
- icoeff[0] = (float)k;
-}
-
-void Tables::initFiltCoeff(float samplerate) {
- for (int j = 0; j < FILTERGRAN; j++) {
- for (int res = 0; res < 31; res++) {
- float tres = resonanceFactor[res];
- initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtCoeff[j][res], tres);
- }
- }
-}
-
-void Tables::initEnvelopes(float samplerate) {
- for (int lf = 0; lf <= 100; lf++) {
- float elf = (float)lf;
-
- // General envelope
- // This formula fits observation of the CM-32L by +/- 0.03s or so for the second time value in the filter,
- // when all other times were 0 and all levels were 100. Note that variations occur depending on the level
- // delta of the section, which we're not fully emulating.
- float seconds = powf(2.0f, (elf / 8.0f) + 7.0f) / 32768.0f;
- int samples = (int)(seconds * samplerate);
- envTime[lf] = samples;
-
- // Cap on envelope times depending on the level delta
- if (elf == 0) {
- envDeltaMaxTime[lf] = 63;
- } else {
- float cap = 11.0f * (float)log(elf) + 64;
- if (cap > 100.0f) {
- cap = 100.0f;
- }
- envDeltaMaxTime[lf] = (int)cap;
- }
-
-
- // This (approximately) represents the time durations when the target level is 0.
- // Not sure why this is a special case, but it's seen to be from the real thing.
- seconds = powf(2, (elf / 8.0f) + 6) / 32768.0f;
- envDecayTime[lf] = (int)(seconds * samplerate);
-
- // I am certain of this: Verified by hand LFO log
- lfoPeriod[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f, (float)lf) * 0.021236044f));
- }
-}
-
-void Tables::initMT32ConstantTables(Synth *synth) {
- int lf;
- synth->printDebug("Initializing Pitch Tables");
- for (lf = -108; lf <= 108; lf++) {
- tvfKeyfollowMult[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f)));
- //synth->printDebug("KT %d = %d", f, keytable[f+108]);
- }
-
- for (int res = 0; res < 31; res++) {
- resonanceFactor[res] = powf((float)res / 30.0f, 5.0f) + 1.0f;
- }
-
- int period = 65536;
-
- for (int ang = 0; ang < period; ang++) {
- int halfang = (period / 2);
- int angval = ang % halfang;
- float tval = (((float)angval / (float)halfang) - 0.5f) * 2;
- if (ang >= halfang)
- tval = -tval;
- sintable[ang] = (Bit16s)(tval * 50.0f) + 50;
- }
-
- int velt, dep;
- float tempdep;
- for (velt = 0; velt < 128; velt++) {
- for (dep = 0; dep < 5; dep++) {
- if (dep > 0) {
- float ff = (float)(exp(3.5f * tvcatconst[dep] * (59.0f - (float)velt)) * tvcatmult[dep]);
- tempdep = 256.0f * ff;
- envTimeVelfollowMult[dep][velt] = (int)tempdep;
- //if ((velt % 16) == 0) {
- // synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep);
- //}
- } else
- envTimeVelfollowMult[dep][velt] = 256;
- }
-
- for (dep = -7; dep < 8; dep++) {
- float fldep = (float)abs(dep) / 7.0f;
- fldep = powf(fldep,2.5f);
- if (dep < 0)
- fldep = fldep * -1.0f;
- pwVelfollowAdd[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0);
- }
- }
-
- for (dep = 0; dep <= 100; dep++) {
- for (velt = 0; velt < 128; velt++) {
- float fdep = (float)dep * 0.000347013f; // Another MT-32 constant
- float fv = ((float)velt - 64.0f)/7.26f;
- float flogdep = powf(10, fdep * fv);
- float fbase;
-
- if (velt > 64)
- synth->tables.tvfVelfollowMult[velt][dep] = (int)(flogdep * 256.0);
- else {
- //lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96));
- fbase = 1 - (powf(((float)dep / 100.0f),.25f) * ((float)(64-velt) / 96.0f));
- synth->tables.tvfVelfollowMult[velt][dep] = (int)(fbase * 256.0);
- }
- //synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]);
- }
- }
-
- for (lf = 0; lf < 128; lf++) {
- float veloFract = lf / 127.0f;
- for (int velsens = 0; velsens <= 100; velsens++) {
- float sensFract = (velsens - 50) / 50.0f;
- if (velsens < 50) {
- tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, veloFract * -sensFract * 127.0f / 20.0f), 8);
- } else {
- tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, (1.0f - veloFract) * sensFract * 127.0f / 20.0f), 8);
- }
- }
- }
-
- for (lf = 0; lf <= 100; lf++) {
- // Converts the 0-100 range used by the MT-32 to volume multiplier
- volumeMult[lf] = FIXEDPOINT_MAKE(powf((float)lf / 100.0f, FLOAT_LN), 7);
- }
-
- for (lf = 0; lf <= 100; lf++) {
- float mv = lf / 100.0f;
- float pt = mv - 0.5f;
- if (pt < 0)
- pt = 0;
-
- // Original (CC version)
- //pwFactor[lf] = (int)(pt * 210.04f) + 128;
-
- // Approximation from sample comparison
- pwFactor[lf] = (int)(pt * 179.0f) + 128;
- }
-
- for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {
- int myRand;
- myRand = rand();
- //myRand = ((myRand - 16383) * 7168) >> 16;
- // This one is slower but works with all values of RAND_MAX
- myRand = (int)((myRand - RAND_MAX / 2) / (float)RAND_MAX * (7168 / 2));
- //FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason
- noiseBuf[i] = (Bit16s)myRand;
- }
-
- float tdist;
- float padjtable[51];
- for (lf = 0; lf <= 50; lf++) {
- if (lf == 0)
- padjtable[lf] = 7;
- else if (lf == 1)
- padjtable[lf] = 6;
- else if (lf == 2)
- padjtable[lf] = 5;
- else if (lf == 3)
- padjtable[lf] = 4;
- else if (lf == 4)
- padjtable[lf] = 4 - (0.333333f);
- else if (lf == 5)
- padjtable[lf] = 4 - (0.333333f * 2);
- else if (lf == 6)
- padjtable[lf] = 3;
- else if ((lf > 6) && (lf <= 12)) {
- tdist = (lf-6.0f) / 6.0f;
- padjtable[lf] = 3.0f - tdist;
- } else if ((lf > 12) && (lf <= 25)) {
- tdist = (lf - 12.0f) / 13.0f;
- padjtable[lf] = 2.0f - tdist;
- } else {
- tdist = (lf - 25.0f) / 25.0f;
- padjtable[lf] = 1.0f - tdist;
- }
- //synth->printDebug("lf %d = padj %f", lf, (double)padjtable[lf]);
- }
-
- float lfp, depf, finalval, tlf;
- int depat, pval, depti;
- for (lf = 0; lf <= 10; lf++) {
- // I believe the depth is cubed or something
-
- for (depat = 0; depat <= 100; depat++) {
- if (lf > 0) {
- depti = abs(depat - 50);
- tlf = (float)lf - padjtable[depti];
- if (tlf < 0)
- tlf = 0;
- lfp = (float)exp(0.713619942f * tlf) / 407.4945111f;
-
- if (depat < 50)
- finalval = 4096.0f * powf(2, -lfp);
- else
- finalval = 4096.0f * powf(2, lfp);
- pval = (int)finalval;
-
- pitchEnvVal[lf][depat] = pval;
- //synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, (double)tlf, (double)lfp);
- } else {
- pitchEnvVal[lf][depat] = 4096;
- //synth->printDebug("lf %d depat %d pval 4096", lf, depat);
- }
- }
- }
- for (lf = 0; lf <= 100; lf++) {
- // It's linear - verified on MT-32 - one of the few things linear
- lfp = ((float)lf * 0.1904f) / 310.55f;
-
- for (depat = 0; depat <= 100; depat++) {
- depf = ((float)depat - 50.0f) / 50.0f;
- //finalval = pow(2, lfp * depf * .5);
- finalval = 4096.0f + (4096.0f * lfp * depf);
-
- pval = (int)finalval;
-
- lfoShift[lf][depat] = pval;
-
- //synth->printDebug("lf %d depat %d pval %x", lf,depat,pval);
- }
- }
-
- for (lf = 0; lf <= 12; lf++) {
- for (int distval = 0; distval < 128; distval++) {
- float amplog, dval;
- if (lf == 0) {
- amplog = 0;
- dval = 1;
- tvaBiasMult[lf][distval] = 256;
- } else {
- /*
- amplog = powf(1.431817011f, (float)lf) / FLOAT_PI;
- dval = ((128.0f - (float)distval) / 128.0f);
- amplog = exp(amplog);
- dval = powf(amplog, dval) / amplog;
- tvaBiasMult[lf][distval] = (int)(dval * 256.0);
- */
- // Lets assume for a second it's linear
-
- // Distance of full volume reduction
- amplog = (float)(12.0f / (float)lf) * 24.0f;
- if (distval > amplog) {
- tvaBiasMult[lf][distval] = 0;
- } else {
- dval = (amplog - (float)distval) / amplog;
- tvaBiasMult[lf][distval] = (int)(dval * 256.0f);
- }
- }
- //synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, (double)dval, tvaBiasMult[lf][distval],(double)amplog);
- }
- }
-
- for (lf = 0; lf <= 14; lf++) {
- for (int distval = 0; distval < 128; distval++) {
- float filval = fabsf((float)((lf - 7) * 12) / 7.0f);
- float amplog, dval;
- if (lf == 7) {
- amplog = 0;
- dval = 1;
- tvfBiasMult[lf][distval] = 256;
- } else {
- //amplog = pow(1.431817011, filval) / FLOAT_PI;
- amplog = powf(1.531817011f, filval) / FLOAT_PI;
- dval = (128.0f - (float)distval) / 128.0f;
- amplog = (float)exp(amplog);
- dval = powf(amplog,dval)/amplog;
- if (lf < 8) {
- tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
- } else {
- dval = powf(dval, 0.3333333f);
- if (dval < 0.01f)
- dval = 0.01f;
- dval = 1 / dval;
- tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
- }
- }
- //synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, (double)dval, tvfBiasMult[lf][distval],(double)amplog);
- }
- }
-}
-
-// Per-note table initialisation follows
-
-static void initSaw(NoteLookup *noteLookup, Bit32s div2) {
- int tmpdiv = div2 << 16;
- for (int rsaw = 0; rsaw <= 100; rsaw++) {
- float fsaw;
- if (rsaw < 50)
- fsaw = 50.0f;
- else
- fsaw = (float)rsaw;
-
- //(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132
- float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f;
- noteLookup->sawTable[rsaw] = (int)(sawfact * (float)tmpdiv) >> 16;
- //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div, rsaw, sawtable[f][rsaw]);
- }
-}
-
-static void initDep(KeyLookup *keyLookup, float f) {
- for (int dep = 0; dep < 5; dep++) {
- if (dep == 0) {
- keyLookup->envDepthMult[dep] = 256;
- keyLookup->envTimeMult[dep] = 256;
- } else {
- float depfac = 3000.0f;
- float ff, tempdep;
- depfac = (float)depexp[dep];
-
- ff = (f - (float)MIDDLEC) / depfac;
- tempdep = powf(2, ff) * 256.0f;
- keyLookup->envDepthMult[dep] = (int)tempdep;
-
- ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]);
- keyLookup->envTimeMult[dep] = (int)(ff * 256.0f);
- }
- }
- //synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", (double)f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]);
-}
-
-Bit16s Tables::clampWF(Synth *synth, const char *n, float ampVal, double input) {
- Bit32s x = (Bit32s)(input * ampVal);
- if (x < -ampVal - 1) {
- synth->printDebug("%s==%d<-WGAMP-1!", n, x);
- x = (Bit32s)(-ampVal - 1);
- } else if (x > ampVal) {
- synth->printDebug("%s==%d>WGAMP!", n, x);
- x = (Bit32s)ampVal;
- }
- return (Bit16s)x;
-}
-
-File *Tables::initWave(Synth *synth, NoteLookup *noteLookup, float ampVal, float div2, File *file) {
- int iDiv2 = (int)div2;
- noteLookup->waveformSize[0] = iDiv2 << 1;
- noteLookup->waveformSize[1] = iDiv2 << 1;
- noteLookup->waveformSize[2] = iDiv2 << 2;
- for (int i = 0; i < 3; i++) {
- if (noteLookup->waveforms[i] == NULL) {
- noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]];
- }
- }
- if (file != NULL) {
- for (int i = 0; i < 3 && file != NULL; i++) {
- size_t len = noteLookup->waveformSize[i];
- for (unsigned int j = 0; j < len; j++) {
- if (!file->readBit16u((Bit16u *)&noteLookup->waveforms[i][j])) {
- synth->printDebug("Error reading wave file cache!");
- file->close();
- file = NULL;
- break;
- }
- }
- }
- }
- if (file == NULL) {
- double sd = DOUBLE_PI / div2;
-
- for (int fa = 0; fa < (iDiv2 << 1); fa++) {
- // sa ranges from 0 to 2PI
- double sa = fa * sd;
-
- // Calculate a sample for the bandlimited sawtooth wave
- double saw = 0.0;
- int sincs = iDiv2 >> 1;
- double sinus = 1.0;
- for (int sincNum = 1; sincNum <= sincs; sincNum++) {
- saw += sin(sinus * sa) / sinus;
- sinus++;
- }
-
- // This works pretty well
- // Multiplied by 0.84 so that the spikes caused by bandlimiting don't overdrive the amplitude
- noteLookup->waveforms[0][fa] = clampWF(synth, "saw", ampVal, -saw / (0.5 * DOUBLE_PI) * 0.84);
- noteLookup->waveforms[1][fa] = clampWF(synth, "cos", ampVal, -cos(sa / 2.0));
- noteLookup->waveforms[2][fa * 2] = clampWF(synth, "cosoff_0", ampVal, -cos(sa - DOUBLE_PI));
- noteLookup->waveforms[2][fa * 2 + 1] = clampWF(synth, "cosoff_1", ampVal, -cos((sa + (sd / 2)) - DOUBLE_PI));
- }
- }
- return file;
-}
-
-static void initFiltTable(NoteLookup *noteLookup, float freq, float rate) {
- for (int tr = 0; tr <= 200; tr++) {
- float ftr = (float)tr;
-
- // Verified exact on MT-32
- if (tr > 100)
- ftr = 100.0f + (powf((ftr - 100.0f) / 100.0f, 3.0f) * 100.0f);
-
- // I think this is the one
- float brsq = powf(10.0f, (tr / 50.0f) - 1.0f);
- noteLookup->filtTable[0][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN);
- if (noteLookup->filtTable[0][tr]>=((FILTERGRAN*15)/16))
- noteLookup->filtTable[0][tr] = ((FILTERGRAN*15)/16);
-
- float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f;
- noteLookup->filtTable[1][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN);
- if (noteLookup->filtTable[1][tr]>=((FILTERGRAN*15)/16))
- noteLookup->filtTable[1][tr] = ((FILTERGRAN*15)/16);
- }
-}
-
-static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) {
- for (int cf = 0; cf <= 100; cf++) {
- float cfmult = (float)cf;
-
- for (int tf = 0;tf <= 100; tf++) {
- float tfadd = (float)tf;
-
- //float freqsum = exp((cfmult + tfadd) / 30.0f) / 4.0f;
- //float freqsum = 0.15f * exp(0.45f * ((cfmult + tfadd) / 10.0f));
-
- float freqsum = powf(2.0f, ((cfmult + tfadd) - 40.0f) / 16.0f);
-
- noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN);
- if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16))
- noteLookup->nfiltTable[cf][tf] = ((FILTERGRAN * 15) / 16);
- }
- }
-}
-
-File *Tables::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry *pcmWaves, File *file) {
- float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));
- float div2 = rate * 2.0f / freq;
- noteLookup->div2 = (int)div2;
-
- if (noteLookup->div2 == 0)
- noteLookup->div2 = 1;
-
- initSaw(noteLookup, noteLookup->div2);
-
- //synth->printDebug("Note %f; freq=%f, div=%f", (double)note, (double)freq, (double) rate / freq);
- file = initWave(synth, noteLookup, WGAMP, div2, file);
-
- // Create the pitch tables
- if (noteLookup->wavTable == NULL)
- noteLookup->wavTable = new Bit32u[synth->controlROMMap->pcmCount];
- double rateMult = 32000.0 / rate;
- double tuner = freq * 65536.0f;
- for (int pc = 0; pc < synth->controlROMMap->pcmCount; pc++) {
- noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);
- }
-
- initFiltTable(noteLookup, freq, rate);
- initNFiltTable(noteLookup, freq, rate);
- return file;
-}
-
-bool Tables::initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float masterTune) {
- const char *NoteNames[12] = {
- "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "
- };
- char filename[64];
- int intRate = (int)rate;
- char version[4] = {0, 0, 0, 5};
- sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, (double)masterTune);
-
- File *file = NULL;
- char header[20];
- memcpy(header, "MT32WAVE", 8);
- int pos = 8;
- // Version...
- for (int i = 0; i < 4; i++)
- header[pos++] = version[i];
- header[pos++] = (char)((intRate >> 24) & 0xFF);
- header[pos++] = (char)((intRate >> 16) & 0xFF);
- header[pos++] = (char)((intRate >> 8) & 0xFF);
- header[pos++] = (char)(intRate & 0xFF);
- int intTuning = (int)masterTune;
- header[pos++] = (char)((intTuning >> 8) & 0xFF);
- header[pos++] = (char)(intTuning & 0xFF);
- header[pos++] = 0;
- header[pos] = (char)((masterTune - intTuning) * 10);
-#if MT32EMU_WAVECACHEMODE < 2
- bool reading = false;
- file = synth->openFile(filename, File::OpenMode_read);
- if (file != NULL) {
- char fileHeader[20];
- if (file->read(fileHeader, 20) == 20) {
- if (memcmp(fileHeader, header, 20) == 0) {
- Bit16u endianCheck;
- if (file->readBit16u(&endianCheck)) {
- if (endianCheck == 1) {
- reading = true;
- } else {
- synth->printDebug("Endian check in %s does not match expected", filename);
- }
- } else {
- synth->printDebug("Unable to read endian check in %s", filename);
- }
- } else {
- synth->printDebug("Header of %s does not match expected", filename);
- }
- } else {
- synth->printDebug("Error reading 16 bytes of %s", filename);
- }
- if (!reading) {
- file->close();
- file = NULL;
- }
- } else {
- synth->printDebug("Unable to open %s for reading", filename);
- }
-#endif
-
- float progress = 0.0f;
- bool abort = false;
- synth->report(ReportType_progressInit, &progress);
- for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) {
- synth->printDebug("Initializing note %s%d", NoteNames[f % 12], (f / 12) - 2);
- NoteLookup *noteLookup = &noteLookups[f - LOWEST_NOTE];
- file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file);
- progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES;
- abort = synth->report(ReportType_progressInit, &progress) != 0;
- if (abort)
- break;
- }
-
-#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2
- if (file == NULL) {
- file = synth->openFile(filename, File::OpenMode_write);
- if (file != NULL) {
- if (file->write(header, 20) == 20 && file->writeBit16u(1)) {
- for (int f = 0; f < NUM_NOTES; f++) {
- for (int i = 0; i < 3 && file != NULL; i++) {
- int len = noteLookups[f].waveformSize[i];
- for (int j = 0; j < len; j++) {
- if (!file->writeBit16u(noteLookups[f].waveforms[i][j])) {
- synth->printDebug("Error writing waveform cache file");
- file->close();
- file = NULL;
- break;
- }
- }
- }
- }
- } else {
- synth->printDebug("Error writing 16-byte header to %s - won't continue saving", filename);
- }
- } else {
- synth->printDebug("Unable to open %s for writing - won't be created", filename);
- }
- }
-#endif
-
- if (file != NULL)
- synth->closeFile(file);
- return !abort;
-}
-
-void Tables::freeNotes() {
- for (int t = 0; t < 3; t++) {
- for (int m = 0; m < NUM_NOTES; m++) {
- if (noteLookups[m].waveforms[t] != NULL) {
- delete[] noteLookups[m].waveforms[t];
- noteLookups[m].waveforms[t] = NULL;
- noteLookups[m].waveformSize[t] = 0;
- }
- if (noteLookups[m].wavTable != NULL) {
- delete[] noteLookups[m].wavTable;
- noteLookups[m].wavTable = NULL;
- }
- }
- }
- initializedMasterTune = 0.0f;
-}
-
-Tables::Tables() {
- initializedSampleRate = 0.0f;
- initializedMasterTune = 0.0f;
- memset(&noteLookups, 0, sizeof(noteLookups));
-}
-
-bool Tables::init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune) {
- if (sampleRate <= 0.0f) {
- synth->printDebug("Bad sampleRate (%f <= 0.0f)", (double)sampleRate);
- return false;
- }
- if (initializedSampleRate == 0.0f) {
- initMT32ConstantTables(synth);
- }
- if (initializedSampleRate != sampleRate) {
- initFiltCoeff(sampleRate);
- initEnvelopes(sampleRate);
- for (int key = 12; key <= 108; key++) {
- initDep(&keyLookups[key - 12], (float)key);
- }
- }
- if (initializedSampleRate != sampleRate || initializedMasterTune != masterTune) {
- freeNotes();
- if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) {
- return false;
- }
- initializedSampleRate = sampleRate;
- initializedMasterTune = masterTune;
- }
- return true;
-}
-
-}
diff --git a/audio/softsynth/mt32/tables.h b/audio/softsynth/mt32/tables.h
deleted file mode 100644
index 9950323e7b..0000000000
--- a/audio/softsynth/mt32/tables.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/* Copyright (c) 2003-2005 Various contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef MT32EMU_TABLES_H
-#define MT32EMU_TABLES_H
-
-namespace MT32Emu {
-
-// Mathematical constants
-const double DOUBLE_PI = 3.1415926535897932384626433832795;
-const double DOUBLE_LN = 2.3025850929940456840179914546844;
-const float FLOAT_PI = 3.1415926535897932384626433832795f;
-const float FLOAT_LN = 2.3025850929940456840179914546844f;
-
-// Filter settings
-const int FILTERGRAN = 512;
-
-// Amplitude of waveform generator
-// FIXME: This value is the amplitude possible whilst avoiding
-// overdriven values immediately after filtering when playing
-// back SQ3MT.MID. Needs to be checked.
-const int WGAMP = 12382;
-
-const int MIDDLEC = 60;
-const int MIDDLEA = 69; // By this I mean "A above middle C"
-
-// FIXME:KG: may only need to do 12 to 108
-// 12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
-// and adjustment for timbre pitch, so the results can be outside that range.
-// Should we move it (by octave) into the 12..108 range, or keep it in 0..127 range,
-// or something else altogether?
-const int LOWEST_NOTE = 12;
-const int HIGHEST_NOTE = 127;
-const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT
-
-class Synth;
-
-struct NoteLookup {
- Bit32u div2;
- Bit32u *wavTable;
- Bit32s sawTable[101];
- int filtTable[2][201];
- int nfiltTable[101][101];
- Bit16s *waveforms[3];
- Bit32u waveformSize[3];
-};
-
-struct KeyLookup {
- Bit32s envTimeMult[5]; // For envelope time adjustment for key pressed
- Bit32s envDepthMult[5];
-};
-
-class Tables {
- float initializedSampleRate;
- float initializedMasterTune;
- void initMT32ConstantTables(Synth *synth);
- static Bit16s clampWF(Synth *synth, const char *n, float ampVal, double input);
- static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div2, File *file);
- bool initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float tuning);
- void initEnvelopes(float sampleRate);
- void initFiltCoeff(float samplerate);
-public:
- // Constant LUTs
- Bit32s tvfKeyfollowMult[217];
- Bit32s tvfVelfollowMult[128][101];
- Bit32s tvfBiasMult[15][128];
- Bit32u tvaVelfollowMult[128][101];
- Bit32s tvaBiasMult[13][128];
- Bit16s noiseBuf[MAX_SAMPLE_OUTPUT];
- Bit16s sintable[65536];
- Bit32s pitchEnvVal[16][101];
- Bit32s envTimeVelfollowMult[5][128];
- Bit32s pwVelfollowAdd[15][128];
- float resonanceFactor[31];
- Bit32u lfoShift[101][101];
- Bit32s pwFactor[101];
- Bit32s volumeMult[101];
-
- // LUTs varying with sample rate
- Bit32u envTime[101];
- Bit32u envDeltaMaxTime[101];
- Bit32u envDecayTime[101];
- Bit32u lfoPeriod[101];
- float filtCoeff[FILTERGRAN][31][8];
-
- // Various LUTs for each note and key
- NoteLookup noteLookups[NUM_NOTES];
- KeyLookup keyLookups[97];
-
- Tables();
- bool init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune);
- File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry *pcmWaves, File *file);
- void freeNotes();
-};
-
-}
-
-#endif