aboutsummaryrefslogtreecommitdiff
path: root/sound/softsynth
diff options
context:
space:
mode:
authorMax Horn2004-12-25 18:34:44 +0000
committerMax Horn2004-12-25 18:34:44 +0000
commitfec3df209601b812034fafed53ef74b7ee732512 (patch)
tree14572de096e66b3720faf67dbd9d3cf3f3926422 /sound/softsynth
parent0d2fa6ecf02d5745db90d78c78e546b3fe62d373 (diff)
downloadscummvm-rg350-fec3df209601b812034fafed53ef74b7ee732512.tar.gz
scummvm-rg350-fec3df209601b812034fafed53ef74b7ee732512.tar.bz2
scummvm-rg350-fec3df209601b812034fafed53ef74b7ee732512.zip
Moved the softsynth midi drivers into a sound/softsynth; amongst other things, this fixes bug #1083058
svn-id: r16316
Diffstat (limited to 'sound/softsynth')
-rw-r--r--sound/softsynth/.cvsignore1
-rw-r--r--sound/softsynth/adlib.cpp1509
-rw-r--r--sound/softsynth/emumidi.h107
-rw-r--r--sound/softsynth/mt32.cpp486
-rw-r--r--sound/softsynth/mt32/.cvsignore1
-rw-r--r--sound/softsynth/mt32/freeverb.cpp312
-rw-r--r--sound/softsynth/mt32/freeverb.h240
-rw-r--r--sound/softsynth/mt32/i386.cpp817
-rw-r--r--sound/softsynth/mt32/i386.h49
-rw-r--r--sound/softsynth/mt32/module.mk17
-rw-r--r--sound/softsynth/mt32/mt32_file.cpp112
-rw-r--r--sound/softsynth/mt32/mt32_file.h67
-rw-r--r--sound/softsynth/mt32/mt32emu.h70
-rw-r--r--sound/softsynth/mt32/part.cpp603
-rw-r--r--sound/softsynth/mt32/part.h104
-rw-r--r--sound/softsynth/mt32/partial.cpp912
-rw-r--r--sound/softsynth/mt32/partial.h142
-rw-r--r--sound/softsynth/mt32/partialManager.cpp266
-rw-r--r--sound/softsynth/mt32/partialManager.h54
-rw-r--r--sound/softsynth/mt32/structures.h282
-rw-r--r--sound/softsynth/mt32/synth.cpp1048
-rw-r--r--sound/softsynth/mt32/synth.h200
-rw-r--r--sound/softsynth/mt32/tables.cpp729
-rw-r--r--sound/softsynth/mt32/tables.h101
-rw-r--r--sound/softsynth/ym2612.cpp907
25 files changed, 9136 insertions, 0 deletions
diff --git a/sound/softsynth/.cvsignore b/sound/softsynth/.cvsignore
new file mode 100644
index 0000000000..39a06683b7
--- /dev/null
+++ b/sound/softsynth/.cvsignore
@@ -0,0 +1 @@
+.deps
diff --git a/sound/softsynth/adlib.cpp b/sound/softsynth/adlib.cpp
new file mode 100644
index 0000000000..97967af89f
--- /dev/null
+++ b/sound/softsynth/adlib.cpp
@@ -0,0 +1,1509 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2004 The ScummVM project
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ */
+
+#include "sound/softsynth/emumidi.h"
+#include "common/util.h"
+#include "sound/fmopl.h"
+
+#ifdef DEBUG_ADLIB
+static int tick;
+#endif
+
+class MidiDriver_ADLIB;
+struct AdlibVoice;
+
+struct InstrumentExtra {
+ byte a, b, c, d, e, f, g, h;
+};
+
+struct AdlibInstrument {
+ byte flags_1;
+ byte oplvl_1;
+ byte atdec_1;
+ byte sustrel_1;
+ byte waveform_1;
+ byte flags_2;
+ byte oplvl_2;
+ byte atdec_2;
+ byte sustrel_2;
+ byte waveform_2;
+ byte feedback;
+ byte flags_a;
+ InstrumentExtra extra_a;
+ byte flags_b;
+ InstrumentExtra extra_b;
+ byte duration;
+
+ AdlibInstrument() { memset(this, 0, sizeof(AdlibInstrument)); }
+};
+
+class AdlibPart : public MidiChannel {
+ friend class MidiDriver_ADLIB;
+
+protected:
+// AdlibPart *_prev, *_next;
+ AdlibVoice *_voice;
+ int16 _pitchbend;
+ byte _pitchbend_factor;
+ int8 _transpose_eff;
+ byte _vol_eff;
+ int8 _detune_eff;
+ byte _modwheel;
+ bool _pedal;
+ byte _program;
+ byte _pri_eff;
+ AdlibInstrument _part_instr;
+
+protected:
+ MidiDriver_ADLIB *_owner;
+ bool _allocated;
+ byte _channel;
+
+ void init(MidiDriver_ADLIB *owner, byte channel);
+ void allocate() { _allocated = true; }
+
+public:
+ AdlibPart() {
+ _voice = 0;
+ _pitchbend = 0;
+ _pitchbend_factor = 2;
+ _transpose_eff = 0;
+ _vol_eff = 0;
+ _detune_eff = 0;
+ _modwheel = 0;
+ _pedal = 0;
+ _program = 0;
+ _pri_eff = 0;
+
+ _owner = 0;
+ _allocated = false;
+ _channel = 0;
+ }
+
+ MidiDriver *device();
+ byte getNumber() { return _channel; }
+ void release() { _allocated = false; }
+
+ void send (uint32 b);
+
+ // Regular messages
+ void noteOff(byte note);
+ void noteOn(byte note, byte velocity);
+ void programChange(byte program);
+ void pitchBend(int16 bend);
+
+ // Control Change messages
+ void controlChange(byte control, byte value);
+ void modulationWheel(byte value);
+ void volume(byte value);
+ void panPosition(byte value) { return; } // Not supported
+ void pitchBendFactor(byte value);
+ void detune(byte value);
+ void priority(byte value);
+ void sustain(bool value);
+ void effectLevel(byte value) { return; } // Not supported
+ void chorusLevel(byte value) { return; } // Not supported
+ void allNotesOff();
+
+ // SysEx messages
+ void sysEx_customInstrument(uint32 type, byte *instr);
+};
+
+// FYI (Jamieson630)
+// It is assumed that any invocation to AdlibPercussionChannel
+// will be done through the MidiChannel base class as opposed to the
+// AdlibPart base class. If this were NOT the case, all the functions
+// listed below would need to be virtual in AdlibPart as well as MidiChannel.
+class AdlibPercussionChannel : public AdlibPart {
+ friend class MidiDriver_ADLIB;
+
+protected:
+ void init(MidiDriver_ADLIB *owner, byte channel);
+
+public:
+ void noteOff(byte note);
+ void noteOn(byte note, byte velocity);
+ void programChange(byte program) { }
+ void pitchBend(int16 bend) { }
+
+ // Control Change messages
+ void controlChange(byte control, byte value) { }
+ void modulationWheel(byte value) { }
+ void pitchBendFactor(byte value) { }
+ void detune(byte value) { }
+ void priority(byte value) { }
+ void sustain(bool value) { }
+
+ // SysEx messages
+ void sysEx_customInstrument(uint32 type, byte *instr) { }
+};
+
+struct Struct10 {
+ byte active;
+ int16 cur_val;
+ int16 count;
+ uint16 max_value;
+ int16 start_value;
+ byte loop;
+ byte table_a[4];
+ byte table_b[4];
+ int8 unk3;
+ int8 modwheel;
+ int8 modwheel_last;
+ uint16 speed_lo_max;
+ uint16 num_steps;
+ int16 speed_hi;
+ int8 direction;
+ uint16 speed_lo;
+ uint16 speed_lo_counter;
+};
+
+struct Struct11 {
+ int16 modify_val;
+ byte param, flag0x40, flag0x10;
+ Struct10 *s10;
+};
+
+struct AdlibVoice {
+ AdlibPart *_part;
+ AdlibVoice *_next, *_prev;
+ byte _waitforpedal;
+ byte _note;
+ byte _channel;
+ byte _twochan;
+ byte _vol_1, _vol_2;
+ int16 _duration;
+
+ Struct10 _s10a;
+ Struct11 _s11a;
+ Struct10 _s10b;
+ Struct11 _s11b;
+
+ AdlibVoice() { memset(this, 0, sizeof(AdlibVoice)); }
+};
+
+struct AdlibSetParams {
+ byte a, b, c, d;
+};
+
+static const byte channel_mappings[9] = {
+ 0, 1, 2, 8,
+ 9, 10, 16, 17,
+ 18
+};
+
+static const byte channel_mappings_2[9] = {
+ 3, 4, 5, 11,
+ 12, 13, 19, 20,
+ 21
+};
+
+static const AdlibSetParams adlib_setparam_table[] = {
+ {0x40, 0, 63, 63}, // level
+ {0xE0, 2, 0, 0}, // unused
+ {0x40, 6, 192, 0}, // level key scaling
+ {0x20, 0, 15, 0}, // modulator frequency multiple
+ {0x60, 4, 240, 15}, // attack rate
+ {0x60, 0, 15, 15}, // decay rate
+ {0x80, 4, 240, 15}, // sustain level
+ {0x80, 0, 15, 15}, // release rate
+ {0xE0, 0, 3, 0}, // waveform select
+ {0x20, 7, 128, 0}, // amp mod
+ {0x20, 6, 64, 0}, // vib
+ {0x20, 5, 32, 0}, // eg typ
+ {0x20, 4, 16, 0}, // ksr
+ {0xC0, 0, 1, 0}, // decay alg
+ {0xC0, 1, 14, 0} // feedback
+};
+
+const byte param_table_1[16] = {
+ 29, 28, 27, 0,
+ 3, 4, 7, 8,
+ 13, 16, 17, 20,
+ 21, 30, 31, 0
+};
+
+const uint16 maxval_table[16] = {
+ 0x2FF, 0x1F, 0x7, 0x3F,
+ 0x0F, 0x0F, 0x0F, 0x3,
+ 0x3F, 0x0F, 0x0F, 0x0F,
+ 0x3, 0x3E, 0x1F, 0
+};
+
+static const uint16 num_steps_table[] = {
+ 1, 2, 4, 5,
+ 6, 7, 8, 9,
+ 10, 12, 14, 16,
+ 18, 21, 24, 30,
+ 36, 50, 64, 82,
+ 100, 136, 160, 192,
+ 240, 276, 340, 460,
+ 600, 860, 1200, 1600
+};
+
+static const byte note_to_f_num[] = {
+ 90, 91, 92, 92, 93, 94, 94, 95,
+ 96, 96, 97, 98, 98, 99, 100, 101,
+ 101, 102, 103, 104, 104, 105, 106, 107,
+ 107, 108, 109, 110, 111, 111, 112, 113,
+ 114, 115, 115, 116, 117, 118, 119, 120,
+ 121, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 157, 158, 159, 160,
+ 161, 162, 163, 165, 166, 167, 168, 169,
+ 171, 172, 173, 174, 176, 177, 178, 180,
+ 181, 182, 184, 185, 186, 188, 189, 190,
+ 192, 193, 194, 196, 197, 199, 200, 202,
+ 203, 205, 206, 208, 209, 211, 212, 214,
+ 215, 217, 218, 220, 222, 223, 225, 226,
+ 228, 230, 231, 233, 235, 236, 238, 240,
+ 242, 243, 245, 247, 249, 251, 252, 254,
+};
+
+static const byte map_gm_to_fm[128][30] = {
+ // 0x00
+{ 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x23 },
+{ 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x23 },
+{ 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x1A },
+{ 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+ // 0x10
+{ 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x18 },
+{ 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x18 },
+{ 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x19 },
+{ 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0A },
+{ 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0B },
+ // 0x20
+{ 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x20 },
+{ 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x28 },
+{ 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x28 },
+{ 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 },
+{ 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+ // 0x30
+{ 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x06 },
+{ 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+ // 0x40
+{ 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+ // 0x50
+{ 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+ // 0x60
+{ 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x1C },
+{ 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x12 },
+{ 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x10 },
+{ 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x07 },
+{ 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0D },
+{ 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x07 },
+{ 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+ // 0x70
+{ 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 },
+{ 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 },
+{ 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 },
+{ 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 },
+{ 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0F },
+{ 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 },
+{ 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x06 },
+{ 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x38 },
+{ 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 },
+{ 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 },
+{ 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 }
+};
+
+static byte gm_percussion_to_fm[39][30] = {
+{ 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x06 },
+{ 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 },
+{ 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x01 },
+{ 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0C },
+{ 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0C },
+{ 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x10 },
+{ 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x06 },
+{ 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 },
+{ 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x10 },
+{ 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x07 },
+{ 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 },
+{ 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x07 },
+{ 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 },
+{ 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 },
+{ 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x08 },
+{ 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x08 },
+{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0D },
+{ 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 },
+{ 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 },
+{ 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 },
+{ 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }
+};
+
+static const byte gm_percussion_lookup[128] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
+ 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0xFF, 0xFF, 0x17, 0x18, 0x19, 0x1A,
+ 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x21, 0x22, 0x23, 0xFF, 0xFF,
+ 0x24, 0x25, 0xFF, 0xFF, 0xFF, 0x26, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static byte lookup_table[64][32];
+static const byte volume_table[] = {
+ 0, 4, 7, 11,
+ 13, 16, 18, 20,
+ 22, 24, 26, 27,
+ 29, 30, 31, 33,
+ 34, 35, 36, 37,
+ 38, 39, 40, 41,
+ 42, 43, 44, 44,
+ 45, 46, 47, 47,
+ 48, 49, 49, 50,
+ 51, 51, 52, 53,
+ 53, 54, 54, 55,
+ 55, 56, 56, 57,
+ 57, 58, 58, 59,
+ 59, 60, 60, 60,
+ 61, 61, 62, 62,
+ 62, 63, 63, 63
+};
+
+static int lookup_volume(int a, int b) {
+ if (b == 0)
+ return 0;
+
+ if (b == 31)
+ return a;
+
+ if (a < -63 || a > 63) {
+ return b * (a + 1) >> 5;
+ }
+
+ if (b < 0) {
+ if (a < 0) {
+ return lookup_table[-a][-b];
+ } else {
+ return -lookup_table[a][-b];
+ }
+ } else {
+ if (a < 0) {
+ return -lookup_table[-a][b];
+ } else {
+ return lookup_table[a][b];
+ }
+ }
+}
+
+static void create_lookup_table() {
+ int i, j;
+ int sum;
+
+ for (i = 0; i < 64; i++) {
+ sum = i;
+ for (j = 0; j < 32; j++) {
+ lookup_table[i][j] = sum >> 5;
+ sum += i;
+ }
+ }
+ for (i = 0; i < 64; i++)
+ lookup_table[i][0] = 0;
+}
+
+////////////////////////////////////////
+//
+// Adlib MIDI driver
+//
+////////////////////////////////////////
+
+class MidiDriver_ADLIB : public MidiDriver_Emulated {
+ friend class AdlibPart;
+ friend class AdlibPercussionChannel;
+
+public:
+ MidiDriver_ADLIB(SoundMixer *mixer);
+
+ int open();
+ void close();
+ void send(uint32 b);
+ void send(byte channel, uint32 b); // Supports higher than channel 15
+ uint32 property(int prop, uint32 param);
+
+ void setPitchBendRange(byte channel, uint range);
+ void sysEx_customInstrument(byte channel, uint32 type, byte *instr);
+
+ MidiChannel *allocateChannel();
+ MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported
+
+
+ // AudioStream API
+ bool isStereo() const { return false; }
+ int getRate() const { return _mixer->getOutputRate(); }
+
+private:
+ bool _game_SmallHeader;
+
+ FM_OPL *_opl;
+ byte *_adlib_reg_cache;
+
+ int _adlib_timer_counter;
+
+ uint16 channel_table_2[9];
+ int _voice_index;
+ int _timer_p;
+ int _timer_q;
+ uint16 curnote_table[9];
+ AdlibVoice _voices[9];
+ AdlibPart _parts[32];
+ AdlibPercussionChannel _percussion;
+
+ void generateSamples(int16 *buf, int len);
+ void onTimer();
+ void part_key_on(AdlibPart *part, AdlibInstrument *instr, byte note, byte velocity);
+ void part_key_off(AdlibPart *part, byte note);
+
+ void adlib_key_off(int chan);
+ void adlib_note_on(int chan, byte note, int mod);
+ void adlib_note_on_ex(int chan, byte note, int mod);
+ int adlib_read_param(int chan, byte data);
+ void adlib_setup_channel(int chan, AdlibInstrument * instr, byte vol_1, byte vol_2);
+ byte adlib_read(byte port) {
+ return _adlib_reg_cache[port];
+ }
+ void adlib_set_param(int channel, byte param, int value);
+ void adlib_key_onoff(int channel);
+ void adlib_write(byte port, byte value);
+ void adlib_playnote(int channel, int note);
+
+ AdlibVoice *allocate_voice(byte pri);
+
+ void mc_off(AdlibVoice * voice);
+
+ static void link_mc(AdlibPart *part, AdlibVoice *voice);
+ void mc_inc_stuff(AdlibVoice *voice, Struct10 * s10, Struct11 * s11);
+ void mc_init_stuff(AdlibVoice *voice, Struct10 * s10, Struct11 * s11, byte flags,
+ InstrumentExtra * ie);
+
+ void struct10_init(Struct10 * s10, InstrumentExtra * ie);
+ static byte struct10_ontimer(Struct10 * s10, Struct11 * s11);
+ static void struct10_setup(Struct10 * s10);
+ static int random_nr(int a);
+ void mc_key_on(AdlibVoice *voice, AdlibInstrument *instr, byte note, byte velocity);
+};
+
+// MidiChannel method implementations
+
+void AdlibPart::init(MidiDriver_ADLIB *owner, byte channel) {
+ _owner = owner;
+ _channel = channel;
+ _pri_eff = 127;
+}
+
+MidiDriver *AdlibPart::device() {
+ return _owner;
+}
+
+void AdlibPart::send(uint32 b) {
+ _owner->send(_channel, b);
+}
+
+void AdlibPart::noteOff(byte note) {
+#ifdef DEBUG_ADLIB
+ debug(6, "%10d: noteOff(%d)", tick, note);
+#endif
+ _owner->part_key_off(this, note);
+}
+
+void AdlibPart::noteOn(byte note, byte velocity) {
+#ifdef DEBUG_ADLIB
+ debug(6, "%10d: noteOn(%d,%d)", tick, note, velocity);
+#endif
+ _owner->part_key_on(this, &_part_instr, note, velocity);
+}
+
+void AdlibPart::programChange(byte program) {
+ if (program > 127)
+ return;
+
+ uint i;
+ uint count = 0;
+ for (i = 0; i < ARRAYSIZE(map_gm_to_fm[0]); ++i)
+ count += map_gm_to_fm[program][i];
+ if (!count)
+ warning("No Adlib instrument defined for GM program %d", (int) program);
+ _program = program;
+ memcpy(&_part_instr, &map_gm_to_fm[program], sizeof(AdlibInstrument));
+}
+
+void AdlibPart::pitchBend(int16 bend) {
+ AdlibVoice *voice;
+
+ _pitchbend = bend;
+ for (voice = _voice; voice; voice = voice->_next) {
+ _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff,
+ (_pitchbend * _pitchbend_factor >> 6) + _detune_eff);
+ }
+}
+
+void AdlibPart::controlChange(byte control, byte value) {
+ switch (control) {
+ case 1: modulationWheel(value); break;
+ case 7: volume(value); break;
+ case 10: break; // Pan position. Not supported.
+ case 16: pitchBendFactor(value); break;
+ case 17: detune(value); break;
+ case 18: priority(value); break;
+ case 64: sustain(value > 0); break;
+ case 91: break; // Effects level. Not supported.
+ case 93: break; // Chorus level. Not supported.
+ case 119: break; // Unknown, used in Simon the Sorcerer 2
+ case 121: break; // Unknown, used in Simon the Sorcerer 1
+ case 123: allNotesOff(); break;
+ default:
+ warning("Adlib: Unknown control change message %d", (int) control);
+ }
+}
+
+void AdlibPart::modulationWheel(byte value) {
+ AdlibVoice *voice;
+
+ _modwheel = value;
+ for (voice = _voice; voice; voice = voice->_next) {
+ if (voice->_s10a.active && voice->_s11a.flag0x40)
+ voice->_s10a.modwheel = _modwheel >> 2;
+ if (voice->_s10b.active && voice->_s11b.flag0x40)
+ voice->_s10b.modwheel = _modwheel >> 2;
+ }
+}
+
+void AdlibPart::volume(byte value) {
+ AdlibVoice *voice;
+
+ _vol_eff = value;
+ for (voice = _voice; voice; voice = voice->_next) {
+ _owner->adlib_set_param(voice->_channel, 0, volume_table[lookup_table[voice->_vol_2][_vol_eff >> 2]]);
+ if (voice->_twochan) {
+ _owner->adlib_set_param(voice->_channel, 13, volume_table[lookup_table[voice->_vol_1][_vol_eff >> 2]]);
+ }
+ }
+}
+
+void AdlibPart::pitchBendFactor(byte value) {
+ AdlibVoice *voice;
+
+ _pitchbend_factor = value;
+ for (voice = _voice; voice; voice = voice->_next) {
+ _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff,
+ (_pitchbend * _pitchbend_factor >> 6) + _detune_eff);
+ }
+}
+
+void AdlibPart::detune(byte value) {
+ AdlibVoice *voice;
+
+ _detune_eff = value;
+ for (voice = _voice; voice; voice = voice->_next) {
+ _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff,
+ (_pitchbend * _pitchbend_factor >> 6) + _detune_eff);
+ }
+}
+
+void AdlibPart::priority(byte value) {
+ _pri_eff = value;
+}
+
+void AdlibPart::sustain(bool value) {
+ AdlibVoice *voice;
+
+ _pedal = value;
+ if (!value) {
+ for (voice = _voice; voice; voice = voice->_next) {
+ if (voice->_waitforpedal)
+ _owner->mc_off(voice);
+ }
+ }
+}
+
+void AdlibPart::allNotesOff() {
+ while (_voice)
+ _owner->mc_off(_voice);
+}
+
+void AdlibPart::sysEx_customInstrument(uint32 type, byte *instr) {
+ if (type == 'ADL ') {
+ AdlibInstrument *i = &_part_instr;
+ memcpy(i, instr, sizeof(AdlibInstrument));
+ }
+}
+
+// MidiChannel method implementations for percussion
+
+void AdlibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) {
+ AdlibPart::init(owner, channel);
+ _pri_eff = 0;
+ _vol_eff = 127;
+}
+
+void AdlibPercussionChannel::noteOff(byte note) {
+ // Jamieson630: Unless I run into a specific instrument that
+ // may require a key off, I'm going to ignore this message.
+ // The rationale is that a percussion instrument should
+ // fade out of its own accord, and the Adlib instrument
+ // definitions used should follow this rule. Since
+ // percussion voices are allocated at the lowest priority
+ // anyway, we know that "hanging" percussion sounds will
+ // not prevent later musical instruments (or even other
+ // percussion sounds) from playing.
+/*
+ _owner->part_key_off(this, note);
+*/
+}
+
+void AdlibPercussionChannel::noteOn(byte note, byte velocity) {
+ byte key = gm_percussion_lookup[note];
+ if (key == 0xFF) {
+ debug(2, "No FM map for GM percussion key %d", (int) note);
+ return;
+ }
+ _owner->part_key_on(this, (AdlibInstrument *) &gm_percussion_to_fm[key], note, velocity);
+}
+
+// MidiDriver method implementations
+
+MidiDriver_ADLIB::MidiDriver_ADLIB(SoundMixer *mixer)
+ : MidiDriver_Emulated(mixer) {
+ uint i;
+
+ _game_SmallHeader = false;
+
+ _adlib_reg_cache = 0;
+
+ _adlib_timer_counter = 0;
+ _voice_index = 0;
+ for (i = 0; i < ARRAYSIZE(curnote_table); ++i) {
+ curnote_table[i] = 0;
+ }
+
+ for (i = 0; i < ARRAYSIZE(_parts); ++i) {
+ _parts[i].init(this, i + ((i >= 9) ? 1 : 0));
+ }
+ _percussion.init(this, 9);
+ _timer_p = 0xD69;
+ _timer_q = 0x411B;
+}
+
+int MidiDriver_ADLIB::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ MidiDriver_Emulated::open();
+
+ int i;
+ AdlibVoice *voice;
+
+ for (i = 0, voice = _voices; i != ARRAYSIZE(_voices); i++, voice++) {
+ voice->_channel = i;
+ voice->_s11a.s10 = &voice->_s10b;
+ voice->_s11b.s10 = &voice->_s10a;
+ }
+
+ _adlib_reg_cache = (byte *)calloc(256, 1);
+
+ _opl = makeAdlibOPL(getRate());
+
+ adlib_write(1, 0x20);
+ adlib_write(8, 0x40);
+ adlib_write(0xBD, 0x00);
+ create_lookup_table();
+
+ _mixer->setupPremix(this);
+
+ return 0;
+}
+
+void MidiDriver_ADLIB::close() {
+ if (!_isOpen)
+ return;
+ _isOpen = false;
+
+ // Detach the premix callback handler
+ _mixer->setupPremix(0);
+
+ uint i;
+ for (i = 0; i < ARRAYSIZE(_voices); ++i) {
+ if (_voices[i]._part)
+ mc_off(&_voices[i]);
+ }
+
+ // Turn off the OPL emulation
+// YM3812Shutdown();
+
+ free(_adlib_reg_cache);
+}
+
+void MidiDriver_ADLIB::send(uint32 b) {
+ send(b & 0xF, b & 0xFFFFFFF0);
+}
+
+void MidiDriver_ADLIB::send(byte chan, uint32 b) {
+ //byte param3 = (byte) ((b >> 24) & 0xFF);
+ byte param2 = (byte) ((b >> 16) & 0xFF);
+ byte param1 = (byte) ((b >> 8) & 0xFF);
+ byte cmd = (byte) (b & 0xF0);
+
+ AdlibPart *part;
+ if (chan == 9)
+ part = &_percussion;
+ else
+ part = &_parts[chan];
+
+ switch (cmd) {
+ case 0x80:// Note Off
+ part->noteOff(param1);
+ break;
+ case 0x90: // Note On
+ part->noteOn(param1, param2);
+ break;
+ case 0xA0: // Aftertouch
+ break; // Not supported.
+ case 0xB0: // Control Change
+ part->controlChange(param1, param2);
+ break;
+ case 0xC0: // Program Change
+ part->programChange(param1);
+ break;
+ case 0xD0: // Channel Pressure
+ break; // Not supported.
+ case 0xE0: // Pitch Bend
+ part->pitchBend((param1 | (param2 << 7)) - 0x2000);
+ break;
+ case 0xF0: // SysEx
+ // We should never get here! SysEx information has to be
+ // sent via high-level semantic methods.
+ warning("MidiDriver_ADLIB: Receiving SysEx command on a send() call");
+ break;
+
+ default:
+ warning("MidiDriver_ADLIB: Unknown send() command 0x%02X", cmd);
+ }
+}
+
+uint32 MidiDriver_ADLIB::property(int prop, uint32 param) {
+ switch (prop) {
+ case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm
+ _game_SmallHeader = (param > 0);
+ if (_game_SmallHeader) {
+ _timer_p = 473;
+ _timer_q = 1000;
+ } else {
+ _timer_p = 0xD69;
+ _timer_q = 0x411B;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) {
+ AdlibVoice *voice;
+ AdlibPart *part = &_parts[channel];
+
+ part->_pitchbend_factor = range;
+ for (voice = part->_voice; voice; voice = voice->_next) {
+ adlib_note_on(voice->_channel, voice->_note + part->_transpose_eff,
+ (part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
+ }
+}
+
+void MidiDriver_ADLIB::sysEx_customInstrument(byte channel, uint32 type, byte *instr) {
+ _parts[channel].sysEx_customInstrument(type, instr);
+}
+
+MidiChannel *MidiDriver_ADLIB::allocateChannel() {
+ AdlibPart *part;
+ uint i;
+
+ for (i = 0; i < ARRAYSIZE(_parts); ++i) {
+ part = &_parts[i];
+ if (!part->_allocated) {
+ part->allocate();
+ return part;
+ }
+ }
+ return NULL;
+}
+
+MidiDriver *MidiDriver_ADLIB_create(SoundMixer *mixer) {
+ return new MidiDriver_ADLIB(mixer);
+}
+
+// All the code brought over from IMuseAdlib
+
+void MidiDriver_ADLIB::adlib_write(byte port, byte value) {
+ if (_adlib_reg_cache[port] == value)
+ return;
+#ifdef DEBUG_ADLIB
+ debug(6, "%10d: adlib_write[%x] = %x", tick, port, value);
+#endif
+ _adlib_reg_cache[port] = value;
+
+ OPLWriteReg(_opl, port, value);
+}
+
+void MidiDriver_ADLIB::generateSamples(int16 *data, int len) {
+ memset(data, 0, sizeof(int16) * len);
+ YM3812UpdateOne(_opl, data, len);
+}
+
+void MidiDriver_ADLIB::onTimer() {
+ AdlibVoice *voice;
+ int i;
+
+ _adlib_timer_counter += _timer_p;
+ while (_adlib_timer_counter >= _timer_q) {
+ _adlib_timer_counter -= _timer_q;
+#ifdef DEBUG_ADLIB
+ tick++;
+#endif
+ voice = _voices;
+ for (i = 0; i != ARRAYSIZE(_voices); i++, voice++) {
+ if (!voice->_part)
+ continue;
+ if (voice->_duration && (voice->_duration -= 0x11) <= 0) {
+ mc_off(voice);
+ return;
+ }
+ if (voice->_s10a.active) {
+ mc_inc_stuff(voice, &voice->_s10a, &voice->_s11a);
+ }
+ if (voice->_s10b.active) {
+ mc_inc_stuff(voice, &voice->_s10b, &voice->_s11b);
+ }
+ }
+ }
+}
+
+void MidiDriver_ADLIB::mc_off(AdlibVoice *voice) {
+ AdlibVoice *tmp;
+
+ adlib_key_off(voice->_channel);
+
+ tmp = voice->_prev;
+
+ if (voice->_next)
+ voice->_next->_prev = tmp;
+ if (tmp)
+ tmp->_next = voice->_next;
+ else
+ voice->_part->_voice = voice->_next;
+ voice->_part = NULL;
+}
+
+void MidiDriver_ADLIB::mc_inc_stuff(AdlibVoice *voice, Struct10 *s10, Struct11 *s11) {
+ byte code;
+ AdlibPart *part = voice->_part;
+
+ code = struct10_ontimer(s10, s11);
+
+ if (code & 1) {
+ switch (s11->param) {
+ case 0:
+ voice->_vol_2 = s10->start_value + s11->modify_val;
+ if (!_game_SmallHeader) {
+ adlib_set_param(voice->_channel, 0,
+ volume_table[lookup_table[voice->_vol_2]
+ [part->_vol_eff >> 2]]);
+ } else {
+ adlib_set_param(voice->_channel, 0, voice->_vol_2);
+ }
+ break;
+ case 13:
+ voice->_vol_1 = s10->start_value + s11->modify_val;
+ if (voice->_twochan && !_game_SmallHeader) {
+ adlib_set_param(voice->_channel, 13,
+ volume_table[lookup_table[voice->_vol_1]
+ [part->_vol_eff >> 2]]);
+ } else {
+ adlib_set_param(voice->_channel, 13, voice->_vol_1);
+ }
+ break;
+ case 30:
+ s11->s10->modwheel = (char)s11->modify_val;
+ break;
+ case 31:
+ s11->s10->unk3 = (char)s11->modify_val;
+ break;
+ default:
+ adlib_set_param(voice->_channel, s11->param,
+ s10->start_value + s11->modify_val);
+ break;
+ }
+ }
+
+ if (code & 2 && s11->flag0x10)
+ adlib_key_onoff(voice->_channel);
+}
+
+void MidiDriver_ADLIB::adlib_key_off(int chan){
+ byte port = chan + 0xB0;
+ adlib_write(port, adlib_read(port) & ~0x20);
+}
+
+byte MidiDriver_ADLIB::struct10_ontimer(Struct10 *s10, Struct11 *s11) {
+ byte result = 0;
+ int i;
+
+ if (s10->count && (s10->count -= 17) <= 0) {
+ s10->active = 0;
+ return 0;
+ }
+
+ i = s10->cur_val + s10->speed_hi;
+ s10->speed_lo_counter += s10->speed_lo;
+ if (s10->speed_lo_counter >= s10->speed_lo_max) {
+ s10->speed_lo_counter -= s10->speed_lo_max;
+ i += s10->direction;
+ }
+ if (s10->cur_val != i || s10->modwheel != s10->modwheel_last) {
+ s10->cur_val = i;
+ s10->modwheel_last = s10->modwheel;
+ i = lookup_volume(i, s10->modwheel_last);
+ if (i != s11->modify_val) {
+ s11->modify_val = i;
+ result = 1;
+ }
+ }
+
+ if (!--s10->num_steps) {
+ s10->active++;
+ if (s10->active > 4) {
+ if (s10->loop) {
+ s10->active = 1;
+ result |= 2;
+ struct10_setup(s10);
+ } else {
+ s10->active = 0;
+ }
+ } else {
+ struct10_setup(s10);
+ }
+ }
+
+ return result;
+}
+
+void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value) {
+ const AdlibSetParams *as;
+ byte port;
+
+ assert(channel >= 0 && channel < 9);
+
+ if (param <= 12) {
+ port = channel_mappings_2[channel];
+ } else if (param <= 25) {
+ param -= 13;
+ port = channel_mappings[channel];
+ } else if (param <= 27) {
+ param -= 13;
+ port = channel;
+ } else if (param == 28 || param == 29) {
+ if (param == 28)
+ value -= 15;
+ else
+ value -= 383;
+ value <<= 4;
+ channel_table_2[channel] = value;
+ adlib_playnote(channel, curnote_table[channel] + value);
+ return;
+ } else {
+ return;
+ }
+
+ as = &adlib_setparam_table[param];
+ if (as->d)
+ value = as->d - value;
+ port += as->a;
+ adlib_write(port, (adlib_read(port) & ~as->c) | (((byte)value) << as->b));
+}
+
+void MidiDriver_ADLIB::adlib_key_onoff(int channel) {
+ byte val;
+ byte port = channel + 0xB0;
+ assert(channel >= 0 && channel < 9);
+
+ val = adlib_read(port);
+ adlib_write(port, val & ~0x20);
+ adlib_write(port, val | 0x20);
+}
+
+void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) {
+ int b, c, d, e, f, g, h;
+ byte t;
+
+ b = s10->unk3;
+ f = s10->active - 1;
+
+ t = s10->table_a[f];
+ e = num_steps_table[lookup_table[t & 0x7F][b]];
+ if (t & 0x80) {
+ e = random_nr(e);
+ }
+ if (e == 0)
+ e++;
+
+ s10->num_steps = s10->speed_lo_max = e;
+
+ if (f != 2) {
+ c = s10->max_value;
+ g = s10->start_value;
+ t = s10->table_b[f];
+ d = lookup_volume(c, (t & 0x7F) - 31);
+ if (t & 0x80) {
+ d = random_nr(d);
+ }
+ if (d + g > c) {
+ h = c - g;
+ } else {
+ h = d;
+ if (d + g < 0)
+ h = -g;
+ }
+ h -= s10->cur_val;
+ } else {
+ h = 0;
+ }
+
+ s10->speed_hi = h / e;
+ if (h < 0) {
+ h = -h;
+ s10->direction = -1;
+ } else {
+ s10->direction = 1;
+ }
+
+ s10->speed_lo = h % e;
+ s10->speed_lo_counter = 0;
+}
+
+void MidiDriver_ADLIB::adlib_playnote(int channel, int note) {
+ byte old, oct, notex;
+ int note2;
+ int i;
+
+ note2 = (note >> 7) - 4;
+ note2 = (note2 < 128) ? note2 : 0;
+
+ oct = (note2 / 12);
+ if (oct > 7)
+ oct = 7 << 2;
+ else
+ oct <<= 2;
+ notex = note2 % 12 + 3;
+
+ old = adlib_read(channel + 0xB0);
+ if (old & 0x20) {
+ old &= ~0x20;
+ if (oct > old) {
+ if (notex < 6) {
+ notex += 12;
+ oct -= 4;
+ }
+ } else if (oct < old) {
+ if (notex > 11) {
+ notex -= 12;
+ oct += 4;
+ }
+ }
+ }
+
+ i = (notex << 3) + ((note >> 4) & 0x7);
+ adlib_write(channel + 0xA0, note_to_f_num[i]);
+ adlib_write(channel + 0xB0, oct | 0x20);
+}
+
+// TODO: Replace this with RandomSource? But if so, please note that this
+// function will be called with negative parameters - getRandomNumber(-1) will
+// crash ScummVM, random_nr(-1) won't.
+
+int MidiDriver_ADLIB::random_nr(int a) {
+ static byte _rand_seed = 1;
+ if (_rand_seed & 1) {
+ _rand_seed >>= 1;
+ _rand_seed ^= 0xB8;
+ } else {
+ _rand_seed >>= 1;
+ }
+ return _rand_seed * a >> 8;
+}
+
+void MidiDriver_ADLIB::part_key_off(AdlibPart *part, byte note) {
+ AdlibVoice *voice;
+
+ for (voice = part->_voice; voice; voice = voice->_next) {
+ if (voice->_note == note) {
+ if (part->_pedal)
+ voice->_waitforpedal = true;
+ else
+ mc_off(voice);
+ }
+ }
+}
+
+void MidiDriver_ADLIB::part_key_on(AdlibPart *part, AdlibInstrument *instr, byte note, byte velocity) {
+ AdlibVoice *voice;
+
+ voice = allocate_voice(part->_pri_eff);
+ if (!voice)
+ return;
+
+ link_mc(part, voice);
+ mc_key_on(voice, instr, note, velocity);
+}
+
+AdlibVoice *MidiDriver_ADLIB::allocate_voice(byte pri) {
+ AdlibVoice *ac, *best = NULL;
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ if (++_voice_index >= 9)
+ _voice_index = 0;
+ ac = &_voices[_voice_index];
+ if (!ac->_part)
+ return ac;
+ if (!ac->_next) {
+ if (ac->_part->_pri_eff <= pri) {
+ pri = ac->_part->_pri_eff;
+ best = ac;
+ }
+ }
+ }
+
+ /* V3 games don't have note priorities, first comes wins. */
+ if (_game_SmallHeader)
+ return NULL;
+
+ if (best)
+ mc_off(best);
+ return best;
+}
+
+void MidiDriver_ADLIB::link_mc(AdlibPart *part, AdlibVoice *voice) {
+ voice->_part = part;
+ voice->_next = (AdlibVoice *)part->_voice;
+ part->_voice = voice;
+ voice->_prev = NULL;
+
+ if (voice->_next)
+ voice->_next->_prev = voice;
+}
+
+void MidiDriver_ADLIB::mc_key_on(AdlibVoice *voice, AdlibInstrument *instr, byte note, byte velocity) {
+ AdlibPart *part = voice->_part;
+ int c;
+ byte vol_1, vol_2;
+
+ voice->_twochan = instr->feedback & 1;
+ voice->_note = note;
+ voice->_waitforpedal = false;
+ voice->_duration = instr->duration;
+ if (voice->_duration != 0)
+ voice->_duration *= 63;
+
+ if (!_game_SmallHeader)
+ vol_1 = (instr->oplvl_1 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_1 >> 2];
+ else
+ vol_1 = 0x3f - (instr->oplvl_1 & 0x3F);
+ if (vol_1 > 0x3F)
+ vol_1 = 0x3F;
+ voice->_vol_1 = vol_1;
+
+ if (!_game_SmallHeader)
+ vol_2 = (instr->oplvl_2 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_2 >> 2];
+ else
+ vol_2 = 0x3f - (instr->oplvl_2 & 0x3F);
+ if (vol_2 > 0x3F)
+ vol_2 = 0x3F;
+ voice->_vol_2 = vol_2;
+
+ c = part->_vol_eff >> 2;
+
+ if (!_game_SmallHeader) {
+ vol_2 = volume_table[lookup_table[vol_2][c]];
+ if (voice->_twochan)
+ vol_1 = volume_table[lookup_table[vol_1][c]];
+ }
+
+ adlib_setup_channel(voice->_channel, instr, vol_1, vol_2);
+ adlib_note_on_ex(voice->_channel, part->_transpose_eff + note, part->_detune_eff + (part->_pitchbend * part->_pitchbend_factor >> 6));
+
+ if (instr->flags_a & 0x80) {
+ mc_init_stuff(voice, &voice->_s10a, &voice->_s11a, instr->flags_a, &instr->extra_a);
+ } else {
+ voice->_s10a.active = 0;
+ }
+
+ if (instr->flags_b & 0x80) {
+ mc_init_stuff(voice, &voice->_s10b, &voice->_s11b, instr->flags_b, &instr->extra_b);
+ } else {
+ voice->_s10b.active = 0;
+ }
+}
+
+void MidiDriver_ADLIB::adlib_setup_channel(int chan, AdlibInstrument *instr, byte vol_1, byte vol_2) {
+ byte port;
+
+ assert(chan >= 0 && chan < 9);
+
+ port = channel_mappings[chan];
+ adlib_write(port + 0x20, instr->flags_1);
+ adlib_write(port + 0x40, (instr->oplvl_1 | 0x3F) - vol_1 );
+ adlib_write(port + 0x60, 0xff & (~instr->atdec_1));
+ adlib_write(port + 0x80, 0xff & (~instr->sustrel_1));
+ adlib_write(port + 0xE0, instr->waveform_1);
+
+ port = channel_mappings_2[chan];
+ adlib_write(port + 0x20, instr->flags_2);
+ adlib_write(port + 0x40, (instr->oplvl_2 | 0x3F) - vol_2 );
+ adlib_write(port + 0x60, 0xff & (~instr->atdec_2));
+ adlib_write(port + 0x80, 0xff & (~instr->sustrel_2));
+ adlib_write(port + 0xE0, instr->waveform_2);
+
+ adlib_write((byte)chan + 0xC0, instr->feedback);
+}
+
+void MidiDriver_ADLIB::adlib_note_on_ex(int chan, byte note, int mod)
+{
+ int code;
+ assert(chan >= 0 && chan < 9);
+ code = (note << 7) + mod;
+ curnote_table[chan] = code;
+ channel_table_2[chan] = 0;
+ adlib_playnote(chan, code);
+}
+
+void MidiDriver_ADLIB::mc_init_stuff(AdlibVoice *voice, Struct10 * s10,
+ Struct11 * s11, byte flags, InstrumentExtra * ie) {
+ AdlibPart *part = voice->_part;
+ s11->modify_val = 0;
+ s11->flag0x40 = flags & 0x40;
+ s10->loop = flags & 0x20;
+ s11->flag0x10 = flags & 0x10;
+ s11->param = param_table_1[flags & 0xF];
+ s10->max_value = maxval_table[flags & 0xF];
+ s10->unk3 = 31;
+ if (s11->flag0x40) {
+ s10->modwheel = part->_modwheel >> 2;
+ } else {
+ s10->modwheel = 31;
+ }
+
+ switch (s11->param) {
+ case 0:
+ s10->start_value = voice->_vol_2;
+ break;
+ case 13:
+ s10->start_value = voice->_vol_1;
+ break;
+ case 30:
+ s10->start_value = 31;
+ s11->s10->modwheel = 0;
+ break;
+ case 31:
+ s10->start_value = 0;
+ s11->s10->unk3 = 0;
+ break;
+ default:
+ s10->start_value = adlib_read_param(voice->_channel, s11->param);
+ }
+
+ struct10_init(s10, ie);
+}
+
+void MidiDriver_ADLIB::struct10_init(Struct10 *s10, InstrumentExtra *ie) {
+ s10->active = 1;
+ if (!_game_SmallHeader) {
+ s10->cur_val = 0;
+ } else {
+ s10->cur_val = s10->start_value;
+ s10->start_value = 0;
+ }
+ s10->modwheel_last = 31;
+ s10->count = ie->a;
+ if (s10->count)
+ s10->count *= 63;
+ s10->table_a[0] = ie->b;
+ s10->table_a[1] = ie->d;
+ s10->table_a[2] = ie->f;
+ s10->table_a[3] = ie->g;
+
+ s10->table_b[0] = ie->c;
+ s10->table_b[1] = ie->e;
+ s10->table_b[2] = 0;
+ s10->table_b[3] = ie->h;
+
+ struct10_setup(s10);
+}
+
+int MidiDriver_ADLIB::adlib_read_param(int chan, byte param) {
+ const AdlibSetParams *as;
+ byte val;
+ byte port;
+
+ assert(chan >= 0 && chan < 9);
+
+ if (param <= 12) {
+ port = channel_mappings_2[chan];
+ } else if (param <= 25) {
+ param -= 13;
+ port = channel_mappings[chan];
+ } else if (param <= 27) {
+ param -= 13;
+ port = chan;
+ } else if (param == 28) {
+ return 0xF;
+ } else if (param == 29) {
+ return 0x17F;
+ } else {
+ return 0;
+ }
+
+ as = &adlib_setparam_table[param];
+ val = adlib_read(port + as->a);
+ val &= as->c;
+ val >>= as->b;
+ if (as->d)
+ val = as->d - val;
+
+ return val;
+}
+
+void MidiDriver_ADLIB::adlib_note_on(int chan, byte note, int mod) {
+ int code;
+ assert(chan >= 0 && chan < 9);
+ code = (note << 7) + mod;
+ curnote_table[chan] = code;
+ adlib_playnote(chan, (int16) channel_table_2[chan] + code);
+}
diff --git a/sound/softsynth/emumidi.h b/sound/softsynth/emumidi.h
new file mode 100644
index 0000000000..34cd4b0983
--- /dev/null
+++ b/sound/softsynth/emumidi.h
@@ -0,0 +1,107 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2004 The ScummVM project
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ */
+
+#include "stdafx.h"
+#include "sound/audiostream.h"
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+
+#define FIXP_SHIFT 16
+
+class MidiDriver_Emulated : public AudioStream, public MidiDriver {
+protected:
+ bool _isOpen;
+ SoundMixer *_mixer;
+
+private:
+ Timer::TimerProc _timerProc;
+ void *_timerParam;
+
+ int _nextTick;
+ int _samplesPerTick;
+
+protected:
+ virtual void generateSamples(int16 *buf, int len) = 0;
+ virtual void onTimer() {}
+
+ int _baseFreq;
+
+public:
+ MidiDriver_Emulated(SoundMixer *mixer) : _mixer(mixer) {
+ _isOpen = false;
+
+ _timerProc = 0;
+ _timerParam = 0;
+
+ _nextTick = 0;
+ _samplesPerTick = 0;
+
+ _baseFreq = 250;
+ }
+
+ int open() {
+ _isOpen = true;
+
+ int d = getRate() / _baseFreq;
+ int r = getRate() % _baseFreq;
+
+ // This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ
+ // but less prone to arithmetic overflow.
+
+ _samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq;
+ return 0;
+ }
+
+ void setTimerCallback(void *timer_param, Timer::TimerProc timer_proc) {
+ _timerProc = timer_proc;
+ _timerParam = timer_param;
+ }
+
+ uint32 getBaseTempo() { return 1000000 / _baseFreq; }
+
+
+ // AudioStream API
+ int readBuffer(int16 *data, const int numSamples) {
+ const int stereoFactor = isStereo() ? 2 : 1;
+ int len = numSamples / stereoFactor;
+ int step;
+
+ do {
+ step = len;
+ if (step > (_nextTick >> FIXP_SHIFT))
+ step = (_nextTick >> FIXP_SHIFT);
+
+ generateSamples(data, step);
+
+ _nextTick -= step << FIXP_SHIFT;
+ if (!(_nextTick >> FIXP_SHIFT)) {
+ if (_timerProc)
+ (*_timerProc)(_timerParam);
+ onTimer();
+ _nextTick += _samplesPerTick;
+ }
+ data += step * stereoFactor;
+ len -= step;
+ } while (len);
+
+ return numSamples;
+ }
+ bool endOfData() const { return false; }
+};
diff --git a/sound/softsynth/mt32.cpp b/sound/softsynth/mt32.cpp
new file mode 100644
index 0000000000..4789e46fc7
--- /dev/null
+++ b/sound/softsynth/mt32.cpp
@@ -0,0 +1,486 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2004 The ScummVM project
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ */
+
+#include "stdafx.h"
+#include "common/scummsys.h"
+
+#ifdef USE_MT32EMU
+
+#include "sound/softsynth/mt32/mt32emu.h"
+
+#include "sound/softsynth/emumidi.h"
+#include "sound/mpu401.h"
+
+#include "common/util.h"
+#include "common/file.h"
+#include "common/config-manager.h"
+
+#include "graphics/font.h"
+#include "graphics/surface.h"
+
+class MidiChannel_MT32 : public MidiChannel_MPU401 {
+ void effectLevel(byte value) { }
+ void chorusLevel(byte value) { }
+};
+
+class MidiDriver_MT32 : public MidiDriver_Emulated {
+private:
+ PlayingSoundHandle _handle;
+ MidiChannel_MT32 _midiChannels[16];
+ uint16 _channelMask;
+ MT32Emu::Synth *_synth;
+
+ int _outputRate;
+
+protected:
+ void generateSamples(int16 *buf, int len);
+
+public:
+ bool _initialising;
+
+ MidiDriver_MT32(SoundMixer *mixer);
+ virtual ~MidiDriver_MT32();
+
+ int open();
+ void close();
+ void send(uint32 b);
+ void setPitchBendRange (byte channel, uint range);
+ void sysEx(byte *msg, uint16 length);
+
+ uint32 property(int prop, uint32 param);
+ MidiChannel *allocateChannel();
+ MidiChannel *getPercussionChannel();
+
+ // AudioStream API
+ bool isStereo() const { return true; }
+ int getRate() const { return _outputRate; }
+};
+
+typedef File SFile;
+
+class MT32File: public MT32Emu::File {
+ SFile file;
+public:
+ bool open(const char *filename, OpenMode mode) {
+ SFile::AccessMode accessMode = mode == OpenMode_read ? SFile::kFileReadMode : SFile::kFileWriteMode;
+ return file.open(filename, accessMode);
+ }
+ void close() {
+ return file.close();
+ }
+ size_t read(void *in, size_t size) {
+ return file.read(in, size);
+ }
+ bool readLine(char *in, size_t size) {
+ return file.gets(in, size) != NULL;
+ }
+ bool readBit8u(MT32Emu::Bit8u *in) {
+ byte b = file.readByte();
+ if (file.eof())
+ return false;
+ *in = b;
+ return true;
+ }
+ size_t write(const void *in, size_t size) {
+ return file.write(in, size);
+ }
+ bool writeBit8u(MT32Emu::Bit8u out) {
+ file.writeByte(out);
+ return !file.ioFailed();
+ }
+ bool isEOF() {
+ return file.eof();
+ }
+};
+
+static int eatSystemEvents() {
+ OSystem::Event event;
+ while (g_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_QUIT:
+ return 1;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static void drawProgress(float progress) {
+ Graphics::Surface surf;
+ uint32 borderColor = 0x2;
+ uint32 fillColor = 0x4;
+ surf.w = g_system->getWidth() / 7 * 5;
+ surf.h = Graphics::g_scummfont.getFontHeight();
+ int x = g_system->getWidth() / 7;
+ int y = g_system->getHeight() / 2 - surf.h / 2;
+ surf.pitch = surf.w;
+ surf.bytesPerPixel = 1;
+ surf.pixels = calloc(surf.w, surf.h);
+ Common::Rect r(surf.w, surf.h);
+ surf.frameRect(r, borderColor);
+ r.grow(-1);
+ r.right = r.left + (uint16)(r.width() * progress);
+ surf.fillRect(r, fillColor);
+ g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, x, y, surf.w, surf.h);
+ g_system->updateScreen();
+ free(surf.pixels);
+}
+
+static void drawMessage(int offset, const Common::String &text) {
+ const Graphics::Font &font(Graphics::g_scummfont);
+ Graphics::Surface surf;
+ uint32 color = 0x2;
+ surf.w = g_system->getWidth();
+ surf.h = font.getFontHeight();
+ surf.pitch = surf.w;
+ surf.bytesPerPixel = 1;
+ surf.pixels = calloc(surf.w, surf.h);
+ font.drawString(&surf, text, 0, 0, surf.w, color, Graphics::kTextAlignCenter);
+ int y = g_system->getHeight() / 2 - font.getFontHeight() / 2 + offset * (font.getFontHeight() + 1);
+ g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, 0, y, surf.w, surf.h);
+ g_system->updateScreen();
+ free(surf.pixels);
+}
+
+static MT32Emu::File *MT32_OpenFile(void *userData, const char *filename, MT32Emu::File::OpenMode mode) {
+ MT32File *file = new MT32File();
+ if (!file->open(filename, mode)) {
+ delete file;
+ return NULL;
+ }
+ return file;
+}
+
+static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) {
+ char buf[512];
+ if (((MidiDriver_MT32 *)userData)->_initialising) {
+ vsprintf(buf, fmt, list);
+ buf[70] = 0; // Truncate to a reasonable length
+ drawMessage(1, buf);
+ }
+ //vdebug(0, fmt, list); // FIXME: Use a higher debug level
+}
+
+static int MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) {
+ switch(type) {
+ case MT32Emu::ReportType_lcdMessage:
+ g_system->displayMessageOnOSD((const char *)reportData);
+ break;
+ case MT32Emu::ReportType_errorControlROM:
+ error("Failed to load MT32_CONTROL.ROM");
+ break;
+ case MT32Emu::ReportType_errorPCMROM:
+ error("Failed to load MT32_PCM.ROM");
+ break;
+ case MT32Emu::ReportType_progressInit:
+ if (((MidiDriver_MT32 *)userData)->_initialising) {
+ drawProgress(*((const float *)reportData));
+ return eatSystemEvents();
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+////////////////////////////////////////
+//
+// MidiDriver_MT32
+//
+////////////////////////////////////////
+
+MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer) : MidiDriver_Emulated(mixer) {
+ _channelMask = 0xFFFF; // Permit all 16 channels by default
+ uint i;
+ for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
+ _midiChannels[i].init(this, i);
+ }
+ _synth = NULL;
+ // A higher baseFreq reduces the length used in generateSamples(),
+ // and means that the timer callback will be called more often.
+ // That results in more accurate timing.
+ _baseFreq = 10000;
+ // Unfortunately bugs in the emulator cause inaccurate tuning
+ // at rates other than 32KHz, thus we produce data at 32KHz and
+ // rely on SoundMixer to convert.
+ _outputRate = 32000; //_mixer->getOutputRate();
+ _initialising = false;
+}
+
+MidiDriver_MT32::~MidiDriver_MT32() {
+ if (_synth != NULL)
+ delete _synth;
+}
+
+int MidiDriver_MT32::open() {
+ MT32Emu::SynthProperties prop;
+
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ MidiDriver_Emulated::open();
+
+ memset(&prop, 0, sizeof(prop));
+ prop.sampleRate = getRate();
+ prop.useReverb = true;
+ prop.useDefaultReverb = false;
+ prop.reverbType = 0;
+ prop.reverbTime = 5;
+ prop.reverbLevel = 3;
+ prop.userData = this;
+ prop.printDebug = MT32_PrintDebug;
+ prop.report = MT32_Report;
+ prop.openFile = MT32_OpenFile;
+ _synth = new MT32Emu::Synth();
+ _initialising = true;
+ const byte dummy_palette[] = {
+ 0, 0, 0, 0,
+ 0, 0, 171, 0,
+ 0, 171, 0, 0,
+ 0, 171, 171, 0,
+ 171, 0, 0, 0
+ };
+
+ g_system->setPalette(dummy_palette, 0, 5);
+ drawMessage(-1, "Initialising MT-32 Emulator");
+ if (!_synth->open(prop))
+ return MERR_DEVICE_NOT_AVAILABLE;
+ _initialising = false;
+ g_system->clearScreen();
+ g_system->updateScreen();
+ _mixer->playInputStream(&_handle, this, false, -1, 255, 0, false, true);
+ return 0;
+}
+
+void MidiDriver_MT32::send(uint32 b) {
+ _synth->playMsg(b);
+}
+
+void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) {
+ if (range > 24) {
+ printf("setPitchBendRange() called with range > 24: %d", range);
+ }
+ byte benderRangeSysex[9];
+ benderRangeSysex[0] = 0x41; // Roland
+ benderRangeSysex[1] = channel;
+ benderRangeSysex[2] = 0x16; // MT-32
+ benderRangeSysex[3] = 0x12; // Write
+ benderRangeSysex[4] = 0x00;
+ benderRangeSysex[5] = 0x00;
+ benderRangeSysex[6] = 0x04;
+ benderRangeSysex[7] = (byte)range;
+ benderRangeSysex[8] = MT32Emu::Synth::calcSysexChecksum(&benderRangeSysex[4], 4, 0);
+ sysEx(benderRangeSysex, 9);
+}
+
+void MidiDriver_MT32::sysEx(byte *msg, uint16 length) {
+ if (msg[0] == 0xf0) {
+ _synth->playSysex(msg, length);
+ } else {
+ _synth->playSysexWithoutFraming(msg, length);
+ }
+}
+
+void MidiDriver_MT32::close() {
+ if (!_isOpen)
+ return;
+ _isOpen = false;
+
+ // Detach the player callback handler
+ setTimerCallback(NULL, NULL);
+ // Detach the mixer callback handler
+ _mixer->stopHandle(_handle);
+
+ _synth->close();
+ delete _synth;
+ _synth = NULL;
+}
+
+void MidiDriver_MT32::generateSamples(int16 *data, int len) {
+ _synth->render(data, len);
+}
+
+uint32 MidiDriver_MT32::property(int prop, uint32 param) {
+ switch (prop) {
+ case PROP_CHANNEL_MASK:
+ _channelMask = param & 0xFFFF;
+ return 1;
+ }
+
+ return 0;
+}
+
+MidiChannel *MidiDriver_MT32::allocateChannel() {
+ MidiChannel_MT32 *chan;
+ uint i;
+
+ for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
+ if (i == 9 || !(_channelMask & (1 << i)))
+ continue;
+ chan = &_midiChannels[i];
+ if (chan->allocate()) {
+ return chan;
+ }
+ }
+ return NULL;
+}
+
+MidiChannel *MidiDriver_MT32::getPercussionChannel() {
+ return &_midiChannels[9];
+}
+
+// This code should be used when calling the timer callback from the mixer thread is undesirable.
+// Note that it results in less accurate timing.
+#if 0
+class MidiEvent_MT32 {
+public:
+ MidiEvent_MT32 *_next;
+ uint32 _msg; // 0xFFFFFFFF indicates a sysex message
+ byte *_data;
+ uint32 _len;
+
+ MidiEvent_MT32(uint32 msg, byte *data, uint32 len) {
+ _msg = msg;
+ if (len > 0) {
+ _data = new byte[len];
+ memcpy(_data, data, len);
+ }
+ _len = len;
+ _next = NULL;
+ }
+
+ MidiEvent_MT32() {
+ if (_len > 0)
+ delete _data;
+ }
+};
+
+class MidiDriver_ThreadedMT32 : public MidiDriver_MT32 {
+private:
+ OSystem::MutexRef _eventMutex;
+ MidiEvent_MT32 *_events;
+ Timer::TimerProc _timer_proc;
+
+ void pushMidiEvent(MidiEvent_MT32 *event);
+ MidiEvent_MT32 *popMidiEvent();
+
+protected:
+ void send(uint32 b);
+ void sysEx(byte *msg, uint16 length);
+
+public:
+ MidiDriver_ThreadedMT32(SoundMixer *mixer);
+ virtual ~MidiDriver_ThreadedMT32();
+
+ void onTimer();
+ void close();
+ void setTimerCallback(void *timer_param, Timer::TimerProc timer_proc);
+};
+
+
+MidiDriver_ThreadedMT32::MidiDriver_ThreadedMT32(SoundMixer *mixer) : MidiDriver_MT32(mixer) {
+ _eventMutex = g_system->createMutex();
+ _events = NULL;
+ _timer_proc = NULL;
+}
+
+MidiDriver_ThreadedMT32::~MidiDriver_ThreadedMT32() {
+ g_system->deleteMutex(_eventMutex);
+}
+
+void MidiDriver_ThreadedMT32::close() {
+ MidiDriver_MT32::close();
+ while ((popMidiEvent() != NULL)) {
+ // Just eat any leftover events
+ }
+}
+
+void MidiDriver_ThreadedMT32::setTimerCallback(void *timer_param, Timer::TimerProc timer_proc) {
+ if (!_timer_proc || !timer_proc) {
+ if (_timer_proc)
+ g_timer->removeTimerProc(_timer_proc);
+ _timer_proc = timer_proc;
+ if (timer_proc)
+ g_timer->installTimerProc(timer_proc, getBaseTempo(), timer_param);
+ }
+}
+
+void MidiDriver_ThreadedMT32::pushMidiEvent(MidiEvent_MT32 *event) {
+ g_system->lockMutex(_eventMutex);
+ if (_events == NULL) {
+ _events = event;
+ } else {
+ MidiEvent_MT32 *last = _events;
+ while (last->_next != NULL)
+ last = last->_next;
+ last->_next = event;
+ }
+ g_system->unlockMutex(_eventMutex);
+}
+
+MidiEvent_MT32 *MidiDriver_ThreadedMT32::popMidiEvent() {
+ MidiEvent_MT32 *event;
+ g_system->lockMutex(_eventMutex);
+ event = _events;
+ if (event != NULL)
+ _events = event->_next;
+ g_system->unlockMutex(_eventMutex);
+ return event;
+}
+
+void MidiDriver_ThreadedMT32::send(uint32 b) {
+ MidiEvent_MT32 *event = new MidiEvent_MT32(b, NULL, 0);
+ pushMidiEvent(event);
+}
+
+void MidiDriver_ThreadedMT32::sysEx(byte *msg, uint16 length) {
+ MidiEvent_MT32 *event = new MidiEvent_MT32(0xFFFFFFFF, msg, length);
+ pushMidiEvent(event);
+}
+
+void MidiDriver_ThreadedMT32::onTimer() {
+ MidiEvent_MT32 *event;
+ while ((event = popMidiEvent()) != NULL) {
+ if (event->_msg == 0xFFFFFFFF) {
+ MidiDriver_MT32::sysEx(event->_data, event->_len);
+ } else {
+ MidiDriver_MT32::send(event->_msg);
+ }
+ delete event;
+ }
+}
+#endif
+
+////////////////////////////////////////
+//
+// MidiDriver_MT32 factory
+//
+////////////////////////////////////////
+
+MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer) {
+ // HACK: It will stay here until engine plugin loader overhaul
+ if (ConfMan.hasKey("extrapath"))
+ File::addDefaultDirectory(ConfMan.get("extrapath"));
+ return new MidiDriver_MT32(mixer);
+}
+
+#endif
diff --git a/sound/softsynth/mt32/.cvsignore b/sound/softsynth/mt32/.cvsignore
new file mode 100644
index 0000000000..39a06683b7
--- /dev/null
+++ b/sound/softsynth/mt32/.cvsignore
@@ -0,0 +1 @@
+.deps
diff --git a/sound/softsynth/mt32/freeverb.cpp b/sound/softsynth/mt32/freeverb.cpp
new file mode 100644
index 0000000000..0c366fe62f
--- /dev/null
+++ b/sound/softsynth/mt32/freeverb.cpp
@@ -0,0 +1,312 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Copyright (C) 2000 Jezar at Dreampoint
+ *
+ * This code is public domain
+ *
+ * Parts of this code are:
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+// Comb filter implementation
+//
+// Written by
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#include "stdafx.h"
+#include "sound/softsynth/mt32/freeverb.h"
+
+comb::comb() {
+ filterstore = 0;
+ bufidx = 0;
+}
+
+void comb::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 comb::setfeedback(float val) {
+ feedback = val;
+}
+
+float comb::getfeedback() {
+ return feedback;
+}
+
+// Allpass filter implementation
+
+allpass::allpass() {
+ bufidx = 0;
+}
+
+void allpass::setbuffer(float *buf, int size) {
+ buffer = buf;
+ bufsize = size;
+}
+
+void allpass::mute() {
+ for (int i = 0; i < bufsize; i++)
+ buffer[i] = 0;
+}
+
+void allpass::setfeedback(float val) {
+ feedback = val;
+}
+
+float allpass::getfeedback() {
+ return feedback;
+}
+
+// Reverb model implementation
+
+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);
+
+ // 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);
+ setwet(initialwet);
+ setroomsize(initialroom);
+ setdry(initialdry);
+ setdamp(initialdamp);
+ setwidth(initialwidth);
+ setmode(initialmode);
+
+ // Buffer will be full of rubbish - so we MUST mute them
+ mute();
+}
+
+void revmodel::mute() {
+ int i;
+
+ if (getmode() >= freezemode)
+ return;
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].mute();
+ combR[i].mute();
+ }
+
+ 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;
+ }
+}
+
+void revmodel::processmix(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 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;
+ }
+}
+
+void revmodel::update() {
+ // Recalculate internal values after parameter change
+
+ int i;
+
+ wet1 = wet * (width / 2 + 0.5f);
+ wet2 = wet * ((1 - width) / 2);
+
+ if (mode >= freezemode) {
+ roomsize1 = 1;
+ damp1 = 0;
+ gain = muted;
+ } else {
+ roomsize1 = roomsize;
+ damp1 = damp;
+ gain = fixedgain;
+ }
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].setfeedback(roomsize1);
+ combR[i].setfeedback(roomsize1);
+ }
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].setdamp(damp1);
+ combR[i].setdamp(damp1);
+ }
+}
+
+// The following get/set functions are not inlined, because
+// speed is never an issue when calling them, and also
+// 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;
+ update();
+}
+
+float revmodel::getroomsize() {
+ return (roomsize - offsetroom) / scaleroom;
+}
+
+void revmodel::setdamp(float value) {
+ damp = value * scaledamp;
+ update();
+}
+
+float revmodel::getdamp() {
+ return damp / scaledamp;
+}
+
+void revmodel::setwet(float value) {
+ wet = value * scalewet;
+ update();
+}
+
+float revmodel::getwet() {
+ return wet / scalewet;
+}
+
+void revmodel::setdry(float value) {
+ dry = value * scaledry;
+}
+
+float revmodel::getdry() {
+ return dry / scaledry;
+}
+
+void revmodel::setwidth(float value) {
+ width = value;
+ update();
+}
+
+float revmodel::getwidth() {
+ return width;
+}
+
+void revmodel::setmode(float value) {
+ mode = value;
+ update();
+}
+
+float revmodel::getmode() {
+ if (mode >= freezemode)
+ return 1;
+ else
+ return 0;
+}
diff --git a/sound/softsynth/mt32/freeverb.h b/sound/softsynth/mt32/freeverb.h
new file mode 100644
index 0000000000..d618f79670
--- /dev/null
+++ b/sound/softsynth/mt32/freeverb.h
@@ -0,0 +1,240 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Copyright (C) 2000 Jezar at Dreampoint
+ *
+ * This code is public domain
+ *
+ * Parts of this code are:
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+// Macro for killing denormalled numbers
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// Based on IS_DENORMAL macro by Jon Watte
+// This code is public domain
+
+#ifndef FREEVERB_H
+#define FREEVERB_H
+
+#define undenormalise(sample) if (((*(unsigned int*)&sample) & 0x7f800000) == 0) sample = 0.0f
+
+// Comb filter class declaration
+
+class comb {
+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;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float comb::process(float input) {
+ float output;
+
+ output = buffer[bufidx];
+ undenormalise(output);
+
+ filterstore = (output * damp2) + (filterstore * damp1);
+ undenormalise(filterstore);
+
+ buffer[bufidx] = input + (filterstore * feedback);
+
+ if (++bufidx >= bufsize)
+ bufidx = 0;
+
+ return output;
+}
+
+// Allpass filter declaration
+
+class allpass {
+public:
+ allpass();
+ void setbuffer(float *buf, int size);
+ 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 allpass::process(float input) {
+ float output;
+ float bufout;
+
+ bufout = buffer[bufidx];
+ undenormalise(bufout);
+
+ output = -input + bufout;
+ buffer[bufidx] = input + (bufout * feedback);
+
+ 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
+
+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();
+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
+
+ // Comb filters
+ 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
diff --git a/sound/softsynth/mt32/i386.cpp b/sound/softsynth/mt32/i386.cpp
new file mode 100644
index 0000000000..e2e4b0f790
--- /dev/null
+++ b/sound/softsynth/mt32/i386.cpp
@@ -0,0 +1,817 @@
+/* Copyright (c) 2003-2004 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__(
+ "movl $1, %%eax \n" \
+ "cpuid \n" \
+ "movl %%edx, %0 \n" \
+ : "=r"(result) : : "eax", "ebx", "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__(
+ "movl $0x80000001, %%eax \n" \
+ "cpuid \n" \
+ "movl %%edx, %0 \n" \
+ : "=r"(result) : : "eax", "ebx", "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)
+ : "xmm1", "xmm2", "xmm3", "memory");
+
+ 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, %%ebx \n" \
+ "movq 0(%%ebx), %%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, %%ebx \n" \
+ "movq 0(%%ebx), %%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, %%ebx \n" \
+ "addl $8, %%eax \n" \
+ " \n" \
+ "movq 0(%%ebx), %%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, %%ebx \n" \
+ "movq 0(%%ebx), %%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", "ebx", "mm1", "mm2", "mm3", "memory");
+
+ 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", "mm1", "mm2", "mm3", "memory");
+}
+
+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", "mm1", "mm2", "memory");
+}
+
+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", "mm1", "mm2", "mm3", "memory");
+}
+
+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", "mm1", "mm2", "memory");
+}
+
+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" \
+ "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" \
+ : : "g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf)
+ : "eax", "ebx", "ecx", "edx", "edi", "esi", "mm1", "mm2", "mm3", "memory");
+}
+
+#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, int revLevel) {
+ 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
+ output *= ResonInv[revLevel];
+ return output;
+}
+
+float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
+ 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
+ output *= ResonInv[revLevel];
+ 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/sound/softsynth/mt32/i386.h b/sound/softsynth/mt32/i386.h
new file mode 100644
index 0000000000..c26fcb4110
--- /dev/null
+++ b/sound/softsynth/mt32/i386.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2003-2004 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, int revLevel);
+float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
+float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
+
+#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/sound/softsynth/mt32/module.mk b/sound/softsynth/mt32/module.mk
new file mode 100644
index 0000000000..2e48efa9d6
--- /dev/null
+++ b/sound/softsynth/mt32/module.mk
@@ -0,0 +1,17 @@
+MODULE := sound/softsynth/mt32
+
+MODULE_OBJS := \
+ sound/softsynth/mt32/mt32_file.o \
+ sound/softsynth/mt32/i386.o \
+ sound/softsynth/mt32/part.o \
+ sound/softsynth/mt32/partial.o \
+ sound/softsynth/mt32/partialManager.o \
+ sound/softsynth/mt32/synth.o \
+ sound/softsynth/mt32/tables.o \
+ sound/softsynth/mt32/freeverb.o
+
+MODULE_DIRS += \
+ sound/softsynth/mt32
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/sound/softsynth/mt32/mt32_file.cpp b/sound/softsynth/mt32/mt32_file.cpp
new file mode 100644
index 0000000000..8cf4e14a7d
--- /dev/null
+++ b/sound/softsynth/mt32/mt32_file.cpp
@@ -0,0 +1,112 @@
+/* Copyright (c) 2003-2004 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 <stdio.h>
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+ bool ANSIFile::open(const char *filename, OpenMode mode) {
+ const char *fmode;
+ if (mode == OpenMode_read) {
+ fmode = "rb";
+ } else {
+ fmode = "wb";
+ }
+ fp = fopen(filename, fmode);
+ return (fp != NULL);
+ }
+
+ void ANSIFile::close() {
+ fclose(fp);
+ }
+
+ size_t ANSIFile::read(void *in, size_t size) {
+ return fread(in, 1, size, fp);
+ }
+
+ bool ANSIFile::readLine(char *in, size_t size) {
+ return fgets(in, (int)size, fp) != NULL;
+ }
+
+ bool ANSIFile::readBit8u(Bit8u *in) {
+ int c = fgetc(fp);
+ if (c == EOF)
+ return false;
+ *in = (Bit8u)c;
+ return true;
+ }
+
+ 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;
+ }
+
+ size_t ANSIFile::write(const void *out, size_t size) {
+ return fwrite(out, 1, size, fp);
+ }
+
+ bool ANSIFile::writeBit8u(Bit8u out) {
+ return fputc(out, fp) != EOF;
+ }
+
+ 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;
+ }
+
+ bool ANSIFile::isEOF() {
+ return feof(fp) != 0;
+ }
+}
diff --git a/sound/softsynth/mt32/mt32_file.h b/sound/softsynth/mt32/mt32_file.h
new file mode 100644
index 0000000000..7ebb6449b6
--- /dev/null
+++ b/sound/softsynth/mt32/mt32_file.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2003-2004 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 <stdio.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 readLine(char *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;
+};
+
+class ANSIFile: public File {
+private:
+ FILE *fp;
+public:
+ bool open(const char *filename, OpenMode mode);
+ void close();
+ size_t read(void *in, size_t size);
+ bool readLine(char *in, size_t size);
+ bool readBit8u(Bit8u *in);
+ size_t write(const void *out, size_t size);
+ bool writeBit8u(unsigned char out);
+ bool isEOF();
+};
+
+}
+
+#endif
diff --git a/sound/softsynth/mt32/mt32emu.h b/sound/softsynth/mt32/mt32emu.h
new file mode 100644
index 0000000000..9fffa721f5
--- /dev/null
+++ b/sound/softsynth/mt32/mt32emu.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2003-2004 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_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
+#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 generage 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
+
+// Configuration
+// The maximum number of partials playing simultaneously
+#define MT32EMU_MAX_PARTIALS 32
+// 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
+
+#include "freeverb.h"
+
+#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"
+
+#endif
diff --git a/sound/softsynth/mt32/part.cpp b/sound/softsynth/mt32/part.cpp
new file mode 100644
index 0000000000..5aee1202b2
--- /dev/null
+++ b/sound/softsynth/mt32/part.cpp
@@ -0,0 +1,603 @@
+/* Copyright (c) 2003-2004 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;
+ if (usePartNum == 8) {
+ // Nasty hack for rhythm
+ patchTemp = NULL;
+ timbreTemp = NULL;
+ } else {
+ sprintf(name, "Part %d", partNum + 1);
+ patchTemp = &synth->mt32ram.patchSettings[partNum];
+ timbreTemp = &synth->mt32ram.timbreSettings[partNum];
+ }
+ currentInstr[0] = 0;
+ currentInstr[10] = 0;
+ volume = voltable[102]; //FIXME:KG: Original was just volume=102; I assume this is intended
+ 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)
+ stopPedalHold();
+ 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, centre 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() {
+ // (Re-)cache all the mapped timbres ahead of time
+ for (unsigned int drumNum = 0; drumNum < 64; drumNum++) {
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 94)
+ 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
+ // FIXME:KG: If I don't have left/right mixed up here, it's pure luck
+ if (pan < 7) {
+ drumPan[drumNum].leftvol = 32767;
+ drumPan[drumNum].rightvol = pan * 4681;
+ } else {
+ drumPan[drumNum].rightvol = 32767;
+ drumPan[drumNum].leftvol = (14 - pan) * 4681;
+ }
+ 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() {
+ 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);
+}
+
+void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
+ for (int m = 0; m < 64; 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 Part::setTimbre(TimbreParam *timbre) {
+ *timbreTemp = *timbre;
+}
+
+unsigned int RhythmPart::getAbsTimbreNum() const {
+ synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm");
+ 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);
+#if 0
+ // Immediately stop all partials on this part (this is apparently *not* the correct behaviour)
+ for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
+ AbortPoly(poly);
+ }
+#endif
+
+ refresh();
+
+ allStop(); //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++) {
+ 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;
+ for (int i = 0; i < 4; i++)
+ cache[t].ampEnv.envlevel[i] = (char)((float)cache[t].ampEnv.envlevel[i] * 1.27f);
+ cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f);
+ float tvelo = ((float)timbre->partial[t].tva.velosens / 100.0f);
+ float velo = fabs(tvelo-0.5f) * 2.0f;
+ velo *= 63.0f;
+ cache[t].ampEnv.velosens = (char)velo;
+ if (tvelo<0.5f)
+ cache[t].ampenvdir = 1;
+ else
+ cache[t].ampenvdir = 0;
+
+ 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;
+ cache[t].ampsustain = cache[t].ampEnv.envlevel[3];
+ cache[t].amplevel = cache[t].ampEnv.level;
+
+ // Calculate and cache filter stuff
+ cache[t].filtEnv = timbre->partial[t].tvf;
+ cache[t].tvfdepth = cache[t].filtEnv.envdkf;
+ 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 = lfotable[(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::setVolume(int vol) {
+ volume = voltable[vol];
+}
+
+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%, centre and right 100%
+ // (But this makes the range somewhat skewed)
+ if (midiPan < 64) {
+ volumesetting.leftvol = 32767;
+ volumesetting.rightvol = (Bit16s)(midiPan * 512);
+ } else if (midiPan == 64) {
+ volumesetting.leftvol = 32767;
+ volumesetting.rightvol = 32767;
+ } else {
+ volumesetting.rightvol = 32767;
+ volumesetting.leftvol = (Bit16s)((127 - midiPan) * 520);
+ }
+ //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
+}
+
+void RhythmPart::playNote(unsigned int key, int vel) {
+ if (key < 24 || key > 87) {
+ synth->printDebug("%s: Attempted to play invalid key %d", name, key);
+ return;
+ }
+ int drumNum = key - 24;
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 94) {
+ 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 Vol %d", name, currentInstr, drumNum, absTimbreNum, vel, key, volume);
+#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 Vol %d", name, currentInstr, vel, key, volume);
+#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 = &volume;
+
+ 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::allStop() {
+ 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/sound/softsynth/mt32/part.h b/sound/softsynth/mt32/part.h
new file mode 100644
index 0000000000..1214ec52f9
--- /dev/null
+++ b/sound/softsynth/mt32/part.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2003-2004 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 PartialManager;
+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];
+ Bit32u volume;
+ 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 void playNote(unsigned int key, int vel);
+ void stopNote(unsigned int key);
+ void allStop();
+ void setVolume(int vol);
+ virtual void setPan(unsigned int midiPan);
+ virtual void setBend(unsigned int midiBend);
+ virtual void setModulation(unsigned int midiModulation);
+ virtual void setProgram(unsigned int patchNum);
+ void setHoldPedal(bool pedalval);
+ void stopPedalHold();
+ virtual void refresh();
+ virtual void refreshTimbre(unsigned int absTimbreNum);
+ void setTimbre(TimbreParam *timbre);
+ virtual unsigned int getAbsTimbreNum() 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[64][4];
+ StereoVolume drumPan[64];
+public:
+ RhythmPart(Synth *synth, unsigned int usePartNum);
+ void refreshTimbre(unsigned int timbreNum);
+ void refresh();
+ 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/sound/softsynth/mt32/partial.cpp b/sound/softsynth/mt32/partial.cpp
new file mode 100644
index 0000000000..32ff9e9af7
--- /dev/null
+++ b/sound/softsynth/mt32/partial.cpp
@@ -0,0 +1,912 @@
+/* Copyright (c) 2003-2004 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"
+
+#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];
+ }
+#endif
+}
+
+int Partial::getOwnerPart() {
+ return ownerPart;
+}
+
+bool Partial::isActive() {
+ return ownerPart > -1;
+}
+
+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, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, 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, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, 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;
+ if (keyfollow < -108)
+ keyfollow = -108;
+ filtVal = keytable[keyfollow + 108];
+ realVal = keytable[(key - MIDDLEC) + 108];
+}
+
+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); // Initialises noteVal, filtVal and realVal
+#if MT32EMU_ACCURATENOTES == 0
+ noteLookup = &noteLookups[noteVal - LOWEST_NOTE];
+#else
+ TableInitialiser::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->PCMList, NULL);
+#endif
+
+ lfoPos = 0;
+ pulsewidth = patchCache->pulsewidth + pwveltable[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];
+ 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 > 127) {
+ ampval = 127;
+ }
+
+ ampval = voltable[ampval];
+ int tmpvel;
+ if (patchCache->ampenvdir == 1)
+ tmpvel = 127 - poly->vel;
+ else
+ tmpvel = poly->vel;
+ ampval = (ampval * ampveltable[tmpvel][(int)patchCache->ampEnv.velosens]) >> 8;
+ //if (envs[EnvelopeType_amp].sustaining)
+ ampEnvVal = ampval;
+ }
+ --envs[EnvelopeType_amp].count;
+ }
+
+ int lfoat = 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 = sintable[lfoatm];
+ lfoat = lfoptable[patchCache->lfodepth][lfoatr];
+ }
+ } else {
+ // Calculate Pitch envelope
+ envval = getPitchEnvelope();
+ int pd = patchCache->pitchEnv.depth;
+ pitchEnvVal = penvtable[pd][envval];
+ }
+
+ int delta;
+ // These two are only for PCM partials, obviously
+ PCMWaveEntry *pcmWave = NULL; // Initialise to please compiler
+ Bit32u pcmAddr = 0; // Initialise to please compiler
+
+ // Wrap positions or end if necessary
+ if (patchCache->PCMPartial) {
+ // PCM partial
+ int len;
+ pcmWave = &synth->PCMList[patchCache->pcm];
+
+ delta = noteLookup->wavTable[patchCache->pcm];
+ pcmAddr = pcmWave->addr;
+ 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 = 0x10707;
+ partialOff.pcmplace %= (Bit16u)(noteLookup->div << 1);
+ }
+
+ // Build delta for position of next sample
+ // Fix delta code
+ Bit64u tdelta = (Bit64u)delta;
+#if MT32EMU_ACCURATENOTES == 0
+ tdelta = (tdelta * fineShift) >> 12;
+#endif
+ tdelta = (tdelta * pitchEnvVal) >> 12;
+ tdelta = (tdelta * lfoat) >> 12;
+ tdelta = (tdelta * bendShift) >> 12;
+ delta = (int)tdelta;
+ Bit32u volume = *poly->volumeptr;
+
+ // Get waveform - either PCM or synthesized sawtooth or square
+ if (ampEnvVal > 0) {
+ if (patchCache->PCMPartial) {
+ // Render PCM sample
+ int ra, rb, dist;
+ Bit32u taddr;
+ if (delta < 0x10000) {
+ // Linear sound interpolation
+ taddr = pcmAddr + partialOff.pcmplace;
+ ra = synth->romfile[taddr];
+ taddr++;
+ if (taddr == pcmAddr + pcmWave->len) {
+ // Past end of PCM
+ if (pcmWave->loop) {
+ rb = synth->romfile[pcmAddr];
+ } else {
+ rb = 0;
+ }
+ } else {
+ rb = synth->romfile[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->romfile[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->romfile[taddr++];
+ }
+ sample = ra / idelta;
+ }
+ } else {
+ // Render synthesised sample
+ Bit32u div = noteLookup->div;
+ int wf = patchCache->waveform;
+ int toff = partialOff.pcmplace;
+ int minorplace = partialOff.pcmoffset >> 14;
+
+ Bit32s filtval = getFiltEnvelope();
+
+ //synth->printDebug("Filtval: %d", filtval);
+
+ if (wf==0) {
+ // Square waveform. Made by combining two pregenerated bandlimited
+ // sawtooth waveforms
+ // Pulse width is not yet correct
+ if (div == 0) {
+ synth->printDebug("ERROR: div=0 generating square wave, this should never happen!");
+ div = 1;
+ }
+ Bit32u ofsA = toff % div;
+ Bit32u ofsB = toff + FIXEDPOINT_UMULT(div, pulsetable[pulsewidth], 8);
+ ofsB = ofsB % div;
+ Bit16s pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace];
+ Bit16s pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace];
+ sample = (pa - pb) * 4;
+ // Non-bandlimited squarewave
+ /*
+ ofs = ((div << 1) * pulsetable[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])
+ sample = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
+ else
+ sample = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
+ sample = sample * 4;
+ // 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 % div;
+
+ Bit32u ofs3 = toff + FIXEDPOINT_UMULT(div, pulsetable[patchCache->pulsewidth], 8);
+ ofs3 = ofs3 % div;
+
+ pa = noteLookup->waveforms[0][ofs];
+ pb = noteLookup->waveforms[0][ofs3];
+ sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP;
+ sample = sample *4;
+ */
+ }
+
+ //Very exact filter
+ if (filtval > ((FILTERGRAN * 15) / 16))
+ filtval = ((FILTERGRAN * 15) / 16);
+ sample = (Bit32s)floor((synth->iirFilter)((float)sample, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance));
+ }
+ }
+
+ // 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", factor, patchCache->benderRange, bendSemitones, 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.0)
+ a = 1.0;
+ if (a<-1.0)
+ a = -1.0;
+ *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.0)
+ a = 1.0;
+ if (a<-1.0)
+ a = -1.0;
+ *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 = smallnoise;
+ }
+
+ 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) >> 16);
+ *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)rightvol) >> 16);
+ mixedBuf++;
+ }
+ return true;
+}
+
+Bit32s Partial::getFiltEnvelope() {
+ int reshigh;
+
+ int cutoff,depth,keyfollow, realfollow;
+
+ EnvelopeStatus *tStat = &envs[EnvelopeType_filt];
+
+ keyfollow = filtVal;
+ realfollow = realVal;
+
+ 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 = lasttimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]];
+ } else {
+ tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(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 = (depth * filveltable[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 = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
+ }
+ } else {
+ // > Bias
+ if (noteVal > bias) {
+ dist = noteVal - bias;
+ cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
+ }
+
+ }
+ //synth->printDebug("Cutoff after %d", cutoff);
+ }
+
+ depth = (depth * noteLookup->fildepTable[patchCache->tvfdepth]) >> 8;
+ reshigh = (reshigh * depth) >> 7;
+
+ Bit32s tmp;
+
+ cutoff *= keyfollow;
+ cutoff /= realfollow;
+
+ reshigh *= keyfollow;
+ reshigh /= realfollow;
+
+ 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 = (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;
+
+ switch(tStat->envstat) {
+ case 0:
+ //Spot for velocity time follow
+ //Only used for first attack
+ tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * veltkeytable[(int)patchCache->ampEnv.envvkf][poly->vel]) >> 8;
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ break;
+ case 3:
+ // Final attack envelope uses same time table as the decay
+ //tStat->envsize = decaytimetable[patchCache->ampEnv.envtime[tStat->envstat]];
+ tStat->envsize = lasttimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]];
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ break;
+ case 4:
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ tc = patchCache->ampsustain;
+ if (!poly->sustain)
+ startDecay(EnvelopeType_amp, tc);
+ else
+ tStat->sustaining = true;
+
+ goto PastCalc;
+ default:
+ //Spot for timekey follow
+ //Only used in subsquent envelope parameters, including the decay
+ tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
+
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ break;
+ }
+
+ tStat->envsize++;
+ tStat->envdist = patchCache->ampEnv.envlevel[tStat->envstat] - tStat->envbase;
+
+ 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->amplevel) >> 7;
+ }
+
+ // 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 = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
+ }
+ } else {
+ // > Bias
+ if (noteVal > bias) {
+ int dist = noteVal - bias;
+ tc = (tc * ampbiastable[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];
+ tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * noteLookup->timekeyTable[(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 = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
+ tStat->envdist = -startval;
+ break;
+ case EnvelopeType_filt:
+ tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
+ tStat->envdist = -startval;
+ break;
+ case EnvelopeType_pitch:
+ tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ;
+ tStat->envdist = patchCache->pitchEnv.level[4] - startval;
+ break;
+ default:
+ break;
+ }
+ tStat->envsize++;
+}
diff --git a/sound/softsynth/mt32/partial.h b/sound/softsynth/mt32/partial.h
new file mode 100644
index 0000000000..0834a64c15
--- /dev/null
+++ b/sound/softsynth/mt32/partial.h
@@ -0,0 +1,142 @@
+/* Copyright (c) 2003-2004 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];
+
+ bool play;
+
+ // Keyfollowed note value
+#if MT32EMU_ACCURATENOTES == 1
+ NoteLookup noteLookupStorage;
+ float noteVal;
+#else
+ int noteVal;
+ int fineShift;
+#endif
+ const NoteLookup *noteLookup; // LUTs for this noteVal
+
+ // Keyfollowed filter values
+ int realVal;
+ int filtVal;
+
+ EnvelopeStatus envs[3];
+
+ 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;
+ PatchCache cachebackup;
+
+ Partial *pair;
+ bool alreadyOutputed;
+ Bit32u age;
+
+ Partial(Synth *synth);
+ ~Partial();
+
+ int getOwnerPart();
+ 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/sound/softsynth/mt32/partialManager.cpp b/sound/softsynth/mt32/partialManager.cpp
new file mode 100644
index 0000000000..776276edda
--- /dev/null
+++ b/sound/softsynth/mt32/partialManager.cpp
@@ -0,0 +1,266 @@
+/* Copyright (c) 2003-2004 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 <memory.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;
+}
diff --git a/sound/softsynth/mt32/partialManager.h b/sound/softsynth/mt32/partialManager.h
new file mode 100644
index 0000000000..ee145d4c9f
--- /dev/null
+++ b/sound/softsynth/mt32/partialManager.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2003-2004 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]);
+};
+
+}
+
+#endif
diff --git a/sound/softsynth/mt32/structures.h b/sound/softsynth/mt32/structures.h
new file mode 100644
index 0000000000..b04d98b0fd
--- /dev/null
+++ b/sound/softsynth/mt32/structures.h
@@ -0,0 +1,282 @@
+/* Copyright (c) 2003-2004 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;
+
+#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; // 0-1 (SQU/SAW)
+ 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 {
+ struct PatchTemp {
+ PatchParam patch;
+ Bit8u outlevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u dummyv[6];
+ } MT32EMU_ALIGN_PACKED patchSettings[8];
+
+ 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 rhythmSettings[86]; // FIXME: Was 64, but let's support the next model...
+
+ TimbreParam MT32EMU_ALIGN_PACKED timbreSettings[8];
+
+ PatchParam MT32EMU_ALIGN_PACKED patches[128];
+
+ struct PaddedTimbre {
+ TimbreParam timbre;
+ Bit8u padding[10];
+ } MT32EMU_ALIGN_PACKED timbres[64 + 64 + 64 + 30]; // 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 system;
+};
+
+struct MemBanks {
+ Bit8u pTemp[8][sizeof(MemParams::PatchTemp)];
+ Bit8u rTemp[86][sizeof(MemParams::RhythmTemp)];
+ Bit8u tTemp[8][sizeof(TimbreParam)];
+ Bit8u patchBank[128][sizeof(PatchParam)];
+ Bit8u timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)];
+ Bit8u systemBank[sizeof(MemParams::SystemArea)];
+ // System memory 0x100000
+ // Display 0x200000
+ // Reset 0x7F0000
+};
+
+#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 ampenvdir;
+ int amplevel;
+ int tvfdepth;
+
+ bool useBender;
+ float benderRange; // 0.0, 1.0, .., 24.0 (semitones)
+
+ TimbreParam::partialParam::envParam pitchEnv;
+ TimbreParam::partialParam::tvaParam ampEnv;
+ TimbreParam::partialParam::tvfParam filtEnv;
+
+ Bit32s ampsustain;
+ 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/sound/softsynth/mt32/synth.cpp b/sound/softsynth/mt32/synth.cpp
new file mode 100644
index 0000000000..1ba543b581
--- /dev/null
+++ b/sound/softsynth/mt32/synth.cpp
@@ -0,0 +1,1048 @@
+/* Copyright (c) 2003-2004 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 <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+const int MAX_SYSEX_SIZE = 512;
+
+float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
+ 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;
+
+ output *= ResonInv[revLevel];
+
+ 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(noteLookups, 0, sizeof(noteLookups));
+ 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
+ if (reverbModel != NULL)
+ 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) {
+ if (myProp.openFile != NULL) {
+ return myProp.openFile(myProp.userData, filename, mode);
+ }
+ char pathBuf[2048];
+ if (myProp.baseDir != NULL) {
+ strcpy(&pathBuf[0], myProp.baseDir);
+ strcat(&pathBuf[0], filename);
+ filename = pathBuf;
+ }
+ ANSIFile *file = new ANSIFile();
+ if (!file->open(filename, mode)) {
+ delete file;
+ return NULL;
+ }
+ return file;
+}
+
+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, sizeof(controlROMData)) == sizeof(controlROMData));
+
+ closeFile(file);
+ return rc;
+}
+
+bool Synth::loadPCMROM(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read); // ROM File
+ if (file == NULL) {
+ return false;
+ }
+ bool rc = true;
+ for (int i = 0; ; 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("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 encoded in dB, convert to 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;
+
+ romfile[i] = (Bit16s)vol;
+
+ }
+ closeFile(file);
+ return rc;
+}
+
+struct ControlROMPCMStruct
+{
+ Bit8u pos;
+ Bit8u len;
+ Bit8u pitchLSB;
+ Bit8u pitchMSB;
+};
+
+void Synth::initPCMList() {
+ ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[0x3000];
+ for (int i = 0; i < 128; 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;
+ //FIXME:KG: Pick a number, any number. The one below sounded best to me in listening tests, but needs to be confirmed.
+ double STANDARDFREQ = 432.1;
+ float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4096.0 - 9.0 / 12.0));
+ //printDebug("%f,%d,%d", pTune, tps[i].pitchCoarse, tps[i].pitchFine);
+ PCMList[i].addr = rAddr;
+ PCMList[i].len = rLen;
+ PCMList[i].loop = rLoop;
+ PCMList[i].tune = rTune;
+ }
+}
+
+void Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem) {
+ TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
+ memcpy(&timbre->common, mem, 14);
+ mem += 14;
+ char drumname[11];
+ strncpy(drumname, timbre->common.name, 10);
+ drumname[10] = 0;
+ for (int t = 0; t < 4; t++) {
+ if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
+ memcpy(&timbre->partial[t], mem, 58);
+ mem += 58;
+ }
+ }
+}
+
+void Synth::initRhythmTimbres() {
+ const Bit8u *drumMap = &controlROMData[0x3200];
+ int timbreNum = 192;
+ for (Bit16u i = 0; i < 60; i += 2) {
+ Bit16u address = (drumMap[i + 1] << 8) | drumMap[i];
+ initRhythmTimbre(timbreNum++, &controlROMData[address]);
+ }
+}
+
+void Synth::initTimbres(Bit16u mapAddress, int startTimbre) {
+ for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) {
+ Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i];
+ address = address + (mapAddress - 0x8000);
+ TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre;
+ memcpy(timbre, &controlROMData[address], sizeof(TimbreParam));
+ }
+}
+
+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));
+ for (int i = 128; i < 192; i++) {
+ // If something sets a patch to point to an uninitialised memory timbre, don't play anything
+ mt32ram.timbres[i].timbre.common.pmute = 0;
+ }
+
+ printDebug("Loading Control ROM");
+ if (!loadControlROM("MT32_CONTROL.ROM")) {
+ printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM");
+ report(ReportType_errorControlROM, &errno);
+ return false;
+ }
+
+ printDebug("Loading PCM ROM");
+ if (!loadPCMROM("MT32_PCM.ROM")) {
+ printDebug("Init Error - Missing MT32_PCM.ROM");
+ report(ReportType_errorPCMROM, &errno);
+ return false;
+ }
+
+ partialManager = new PartialManager(this);
+
+ printDebug("Initialising PCM List");
+ initPCMList();
+
+ printDebug("Initialising Timbre Bank A");
+ initTimbres(0x8000, 0);
+
+ printDebug("Initialising Timbre Bank B");
+ initTimbres(0xC000, 64);
+
+ printDebug("Initialising Timbre Bank R");
+ initRhythmTimbres();
+
+ printDebug("Initialising Rhythm Temp");
+ memcpy(mt32ram.rhythmSettings, &controlROMData[0x741C], 344);
+
+ printDebug("Initialising 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("Initialising System");
+ //FIXME: Confirm that these are all correct
+ // The MT-32 manual claims that "Standard pitch" is 442Hz.
+ // I assume they mean this is the MT-32 default pitch, and not concert pitch,
+ // since the latter has been internationally defined as 440Hz for decades.
+ // Regardless, I'm setting the default masterTune to 440Hz
+ mt32ram.system.masterTune = 0x40;
+ mt32ram.system.reverbMode = 0;
+ mt32ram.system.reverbTime = 5;
+ mt32ram.system.reverbLevel = 3;
+ memcpy(mt32ram.system.reserveSettings, &controlROMData[0x57E5], 9);
+ 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;
+ if (!refreshSystem())
+ return false;
+
+ for (int i = 0; i < 8; i++) {
+ mt32ram.patchSettings[i].outlevel = 80;
+ mt32ram.patchSettings[i].panpot = controlROMData[0x5800 + i];
+ memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv));
+ parts[i] = new Part(this, i);
+ parts[i]->setProgram(controlROMData[0x57EE + 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;
+
+ TableInitialiser::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;
+ }
+ isOpen = false;
+}
+
+void Synth::playMsg(Bit32u msg) {
+ unsigned char code = (unsigned char)((msg & 0xf0) >> 4);
+ unsigned char chan = (unsigned char)(msg & 0xf);
+ unsigned char note = (unsigned char)((msg & 0xff00) >> 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) {
+ 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
+ //if (part!=3) return;
+ //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]->setVolume(velocity);
+ break;
+ case 0x40: // Hold pedal
+ //printDebug("Hold pedal set: %d", velocity);
+ parts[part]->setHoldPedal(velocity>=64);
+ break;
+
+ case 0x79: // Reset all controllers
+ printDebug("Reset all controllers (NYI)");
+ break;
+
+ case 0x7B: // All notes off
+ //printDebug("All notes off");
+ parts[part]->allStop();
+ 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 < 3) {
+ printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
+ }
+ if (sysex[0] != 0xf0) {
+ printDebug("playSysex: Message lacks start-of-sysex (0xf0)");
+ return;
+ }
+ if (sysex[len - 1] != 0xf7) {
+ printDebug("playSysex: Message lacks end-of-sysex (0xf7)");
+ return;
+ }
+ playSysexWithoutFraming(sysex + 1, len - 2);
+}
+
+void Synth::playSysexWithoutFraming(const Bit8u * sysex, Bit32u len) {
+ if (len < 4) {
+ printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
+ return;
+ }
+ if (sysex[0] != 0x41) {
+ 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] == 0x14) {
+ printDebug("playSysexWithoutFraming: Header is intended for Roland 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] != 0x16) {
+ printDebug("playSysexWithoutFraming: Header not intended for MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ if (sysex[3] != 0x12) {
+ printDebug("playSysexWithoutFraming: Unsupported command %02x", sysex[3]);
+ return;
+ }
+ playSysexWithoutHeader(sysex[1], sysex + 4, len - 4);
+}
+
+// MEMADDR() converts from sysex-padded, SYSEXMEMADDR converts to it
+// Roland provides documentation using the sysex-padded addresses, so we tend to use that int code and output
+#define MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f))
+#define SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f))
+
+#define NUMTOUCHED(x,y) (((x) + sizeof(y) - 1) / sizeof(y))
+
+void Synth::playSysexWithoutHeader(unsigned char device, 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
+ Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
+ addr = MEMADDR(addr);
+ sysex += 3;
+ len -= 3;
+ //printDebug("Sysex addr: 0x%06x", SYSEXMEMADDR(addr));
+ // NOTE: Please keep both lower and upper bounds in each check, for ease of reading
+ if (device < 0x10) {
+ printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, SYSEXMEMADDR(addr));
+ if (/*addr >= MEMADDR(0x000000) && */addr < 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 += MEMADDR(0x030000) + offset;
+ } else if (/*addr >= 0x010000 && */ addr < MEMADDR(0x020000)) {
+ addr += MEMADDR(0x030110) - MEMADDR(0x010000);
+ } else if (/*addr >= 0x020000 && */ addr < 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 += MEMADDR(0x040000) - MEMADDR(0x020000) + offset;
+ } else {
+ printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, SYSEXMEMADDR(addr));
+ return;
+ }
+ }
+ if (addr >= MEMADDR(0x030000) && addr < MEMADDR(0x030110)) {
+ int off = addr - MEMADDR(0x030000);
+ if (off + len > sizeof(mt32ram.patchSettings)) {
+ printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
+ return;
+ }
+ int firstPart = off / sizeof(MemParams::PatchTemp);
+ off %= sizeof(MemParams::PatchTemp);
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.patchSettings[firstPart])[off + m] = sysex[m];
+ //printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);
+
+ int lastPart = firstPart + NUMTOUCHED(off + len, MemParams::PatchTemp) - 1;
+ for (int i = firstPart; i <= lastPart; 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)", firstPart, lastPart, off, off + len, i, absTimbreNum, timbreName);
+ if (parts[i] != NULL) {
+ if (i == firstPart && off > 2) {
+ printDebug(" (Not updating timbre, since those values weren't touched)");
+ } else {
+ // Not sure whether we should do this at all, really.
+ parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
+ }
+ parts[i]->refresh();
+ }
+ }
+ } else if (addr >= MEMADDR(0x030110) && addr < MEMADDR(0x040000)) {
+ int off = addr - MEMADDR(0x030110);
+ if (off + len > sizeof(mt32ram.rhythmSettings)) {
+ printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
+ return;
+ }
+ int firstDrum = off / sizeof(MemParams::RhythmTemp);
+ off %= sizeof(MemParams::RhythmTemp);
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.rhythmSettings[firstDrum])[off + m] = sysex[m];
+ int lastDrum = firstDrum + NUMTOUCHED(off + len, MemParams::RhythmTemp) - 1;
+ for (int i = firstDrum; i <= lastDrum; 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)", firstDrum, lastDrum, 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();
+ }
+ } else if (addr >= MEMADDR(0x040000) && addr < MEMADDR(0x050000)) {
+ int off = addr - MEMADDR(0x040000);
+ if (off + len > sizeof(mt32ram.timbreSettings)) {
+ printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
+ return;
+ }
+ int firstPart = off / sizeof(TimbreParam);
+ off %= sizeof(TimbreParam);
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.timbreSettings[firstPart])[off + m] = sysex[m];
+ int lastPart = firstPart + NUMTOUCHED(off + len, TimbreParam) - 1;
+ for (int i = firstPart; i <= lastPart; 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)", firstPart, lastPart, off, off + len, i, instrumentName);
+ if (parts[i] != NULL) {
+ parts[i]->refresh();
+ }
+ }
+ }
+ else if (addr >= MEMADDR(0x050000) && addr < MEMADDR(0x060000)) {
+ int off = addr - MEMADDR(0x050000);
+ if (off + len > sizeof(mt32ram.patches)) {
+ printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
+ return;
+ }
+ int firstPatch = off / sizeof(PatchParam);
+ off %= sizeof(PatchParam);
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.patches[firstPatch])[off + m] = sysex[m];
+ int lastPatch = firstPatch + NUMTOUCHED(off + len, PatchParam) - 1;
+ for (int i = firstPatch; i <= lastPatch; 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", firstPatch, lastPatch, 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();
+ }
+ }
+ }
+ */
+ }
+ } else if (addr >= MEMADDR(0x080000) && addr < MEMADDR(0x090000)) {
+ // Timbres
+ int off = addr - MEMADDR(0x080000);
+ if (off + len > sizeof(MemParams::PaddedTimbre) * 64) {
+ // You can only write to one group at a time
+ printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
+ return;
+ }
+ unsigned int firstTimbre = off / sizeof (MemParams::PaddedTimbre);
+ off %= sizeof (MemParams::PaddedTimbre);
+ firstTimbre += 128;
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.timbres[firstTimbre])[off + m] = sysex[m];
+ unsigned int lastTimbre = firstTimbre + NUMTOUCHED(len + off, MemParams::PaddedTimbre) - 1;
+ for (unsigned int i = firstTimbre; i <= lastTimbre; 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\"", firstTimbre, lastTimbre, 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);
+ }
+ }
+ }
+ } else if (addr >= MEMADDR(0x100000) && addr < MEMADDR(0x200000)) {
+ int off = addr - MEMADDR(0x100000);
+ if (off + len > sizeof(mt32ram.system)) {
+ printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
+ return;
+ }
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.system)[m + off] = sysex[m];
+
+ report(ReportType_devReconfig, NULL);
+
+ printDebug("WRITE-SYSTEM:");
+ refreshSystem();
+ } else if (addr == MEMADDR(0x200000)) {
+ char buf[MAX_SYSEX_SIZE];
+ if (len > MAX_SYSEX_SIZE - 1) {
+ printDebug("WRITE-LCD sysex length (%d) exceeded MAX_SYSEX_SIZE (%d) - 1; truncating", len, MAX_SYSEX_SIZE);
+ len = MAX_SYSEX_SIZE - 1;
+ }
+ memcpy(&buf, &sysex[0], len);
+ buf[len] = 0;
+ printDebug("WRITE-LCD: %s", buf);
+ report(ReportType_lcdMessage, buf);
+ } else if (addr >= MEMADDR(0x7f0000)) {
+ printDebug("Reset");
+ report(ReportType_devReset, NULL);
+ partialManager->deactivateAll();
+ mt32ram = mt32default;
+ for (int i = 0; i < 9; i++) {
+ parts[i]->refresh();
+ }
+ isEnabled = false;
+ } else {
+ printDebug("Sysex write to unrecognised address %06x", SYSEXMEMADDR(addr));
+ }
+}
+
+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]->allStop();
+ } 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", 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 * 327);
+ if (!TableInitialiser::initMT32Tables(this, PCMList, (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) {
+ Bit32u m;
+
+ partialManager->ageAll();
+
+ if (myProp.useReverb) {
+ bool hasOutput = false;
+ 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);
+ hasOutput = true;
+ }
+ }
+ }
+ // No point in doing reverb on a mute buffer...
+ if (hasOutput) {
+ 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
+}
+
+}
diff --git a/sound/softsynth/mt32/synth.h b/sound/softsynth/mt32/synth.h
new file mode 100644
index 0000000000..1501593cbd
--- /dev/null
+++ b/sound/softsynth/mt32/synth.h
@@ -0,0 +1,200 @@
+/* Copyright (c) 2003-2004 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 <stdarg.h>
+
+class revmodel;
+
+namespace MT32Emu {
+
+const int ROMSIZE = 512 * 1024;
+const int PCMSIZE = ROMSIZE / 2;
+const int GRAN = 512;
+
+class File;
+class TableInitialiser;
+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, int revLevel);
+
+class Synth {
+friend class Part;
+friend class RhythmPart;
+friend class Partial;
+friend class TableInitialiser;
+private:
+ bool isEnabled;
+
+ iir_filter_type iirFilter;
+
+ PCMWaveEntry PCMList[128];
+
+ Bit8u controlROMData[64 * 1024];
+ Bit16s romfile[PCMSIZE + GRAN];
+ Bit8s chantable[32];
+
+ #if MT32EMU_MONITOR_PARTIALS == 1
+ static Bit32s samplepos = 0;
+ #endif
+
+ 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 playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
+ void playSysexWithoutHeader(unsigned char channel, const Bit8u *sysex, Bit32u len);
+
+ 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);
+
+ void initPCMList();
+ void initRhythmTimbres();
+ void initTimbres(Bit16u mapAddress, int startTimbre);
+ void initRhythmTimbre(int drumNum, const Bit8u *mem);
+ 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, ...);
+
+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);
+
+ // 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);
+
+ // 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);
+};
+
+}
+
+#endif
diff --git a/sound/softsynth/mt32/tables.cpp b/sound/softsynth/mt32/tables.cpp
new file mode 100644
index 0000000000..4a17165733
--- /dev/null
+++ b/sound/softsynth/mt32/tables.cpp
@@ -0,0 +1,729 @@
+/* Copyright (c) 2003-2004 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 <string.h>
+#include <math.h>
+
+#include "mt32emu.h"
+
+#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))
+
+namespace MT32Emu {
+
+//Amplitude time velocity follow exponential coefficients
+const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
+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};
+
+static float initialisedSampleRate = 0.0f;
+static float initialisedMasterTune = 0.0f;
+
+Bit16s smallnoise[MAX_SAMPLE_OUTPUT];
+
+// Some optimization stuff
+Bit32s keytable[217];
+Bit16s sintable[65536];
+Bit32u lfotable[101];
+Bit32s penvtable[16][101];
+Bit32s filveltable[128][101];
+Bit32s veltkeytable[5][128];
+Bit32s pulsetable[101];
+Bit32s pulseoffset[101];
+Bit32s ampbiastable[13][128];
+Bit32s fbiastable[15][128];
+float filtcoeff[FILTERGRAN][31][8];
+Bit32s finetable[201];
+Bit32u lfoptable[101][101];
+Bit32s ampveltable[128][64];
+Bit32s pwveltable[15][128];
+Bit32s envtimetable[101];
+Bit32s decaytimetable[101];
+Bit32s lasttimetable[101];
+Bit32s voltable[128];
+float ResonFactor[31];
+float ResonInv[31];
+
+NoteLookup noteLookups[NUM_NOTES];
+
+// 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;
+}
+
+static void initFiltCoeff(float samplerate) {
+ for (int j = 0; j < FILTERGRAN; j++) {
+ for (int res = 0; res < 31; res++) {
+ float tres = ResonFactor[res];
+ initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtcoeff[j][res], tres);
+ }
+ }
+}
+
+static void initEnvelopes(float samplerate) {
+ for (int lf = 0; lf <= 100; lf++) {
+ float elf = (float)lf;
+
+ // General envelope
+ float logtime = elf * 0.088362939f;
+ envtimetable[lf] = (int)((exp(logtime)/312.12) * (float)samplerate);
+
+ // Decay envelope -- shorter for some reason
+ // This is also the timing for the envelope right before the
+ // amp and filter envelope sustains
+
+ lasttimetable[lf] = decaytimetable[lf] = (int)((exp(logtime)/(312.12*2)) * (float)samplerate);
+ //lasttimetable[lf] = (int)((exp(logtime)/(312.12*6)) * (float)samplerate);
+
+ float mv = (float)lf / 100.0f;
+ float pt = mv - 0.5f;
+ if (pt < 0)
+ pt = 0;
+
+ pulsetable[lf] = (int)(pt * 215.04f) + 128;
+
+ // I am certain of this: Verified by hand LFO log
+ lfotable[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f,(float)lf) * 0.021236044f));
+
+ //LOG(LOG_ERROR|LOG_MISC,"lf %d = lfo %d pulsetable %d", lf, lfotable[lf], pulsetable[lf]);
+ }
+}
+
+void TableInitialiser::initMT32ConstantTables(Synth *synth) {
+ if (initialisedSampleRate > 0.0f) {
+ return;
+ }
+ int lf;
+ synth->printDebug("Initialising Pitch Tables");
+ for (lf = -108; lf <= 108; lf++) {
+ keytable[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f)));
+ //synth->printDebug("KT %d = %d", f, keytable[f+108]);
+ }
+
+ int res;
+ float fres;
+ for (res = 0; res < 31; res++) {
+ fres = (float)res / 30.0f;
+ ResonFactor[res] = (powf(2.0f, logf(powf(fres, 16.0f))) * 2.5f) + 1.0f;
+ ResonInv[res] = 1 / ResonFactor[res];
+ }
+
+ 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;
+ veltkeytable[dep][velt] = (int)tempdep;
+ //if ((velt % 16) == 0) {
+ // synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep);
+ //}
+ } else
+ veltkeytable[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;
+ pwveltable[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)
+ filveltable[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));
+ filveltable[velt][dep] = (int)(fbase * 256.0);
+ }
+ //synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]);
+ }
+ }
+
+ for (lf = 0; lf <= 200; lf++) {
+ //FIXME:KG: I'm fairly sure this is wrong... lf=100 should yield no fine-tuning (4096)?
+ finetable[lf] = (int)((powf(2.0f, (((float)lf / 200.0f) - 1.0f) / 12.0f)) * 4096.0f);
+
+ // FIXME:KG: This now gives a range of -1 .. 1 semitone. Should be correct, but check
+ //finetable[lf] = (int)((powf(2.0f, (((float)lf / 100.0f) - 1.0f) / 12.0f)) * 4096.0f);
+ }
+
+ float lff;
+ for (lf = 0; lf < 128; lf++) {
+ for (velt = 0; velt < 64; velt++) {
+ lff = 1 - (powf(((128.0f - (float)lf) / 64.0f), 0.25f) * ((float)velt / 96));
+ ampveltable[lf][velt] = (int)(lff * 256.0);
+ //synth->printDebug("Ampveltable: %d, %d = %d", lf, velt, ampveltable[lf][velt]);
+ }
+ }
+
+ for (lf = 0; lf < 128; lf++) {
+ // Converts MIDI velocity to volume.
+ voltable[lf] = FIXEDPOINT_MAKE(powf((float)lf / 127.0f, FLOAT_LN), 7);
+ }
+ for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {
+ int myRand;
+ myRand = rand();
+ int origRand = myRand;
+ //myRand = ((myRand - 16383) * WGAMP) >> 16;
+ // This one is slower but works with all values of RAND_MAX
+ myRand = (int)((origRand - RAND_MAX / 2) / (float)RAND_MAX * (WGAMP / 2));
+ //FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason
+ smallnoise[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, 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 = expf(0.713619942f * tlf) / 407.4945111f;
+
+ if (depat < 50)
+ finalval = 4096.0f * powf(2, -lfp);
+ else
+ finalval = 4096.0f * powf(2, lfp);
+ pval = (int)finalval;
+
+ penvtable[lf][depat] = pval;
+ //synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp);
+ } else {
+ penvtable[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;
+
+ lfoptable[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;
+ ampbiastable[lf][distval] = 256;
+ } else {
+ amplog = powf(1.431817011f, (float)lf) / FLOAT_PI;
+ dval = ((128.0f - (float)distval) / 128.0f);
+ amplog = expf(amplog);
+ dval = powf(amplog, dval) / amplog;
+ ampbiastable[lf][distval] = (int)(dval * 256.0);
+ }
+ //synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, ampbiastable[lf][distval],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;
+ fbiastable[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 = expf(amplog);
+ dval = powf(amplog,dval)/amplog;
+ if (lf < 8) {
+ fbiastable[lf][distval] = (int)(dval * 256.0f);
+ } else {
+ dval = powf(dval, 0.3333333f);
+ if (dval < 0.01f)
+ dval = 0.01f;
+ dval = 1 / dval;
+ fbiastable[lf][distval] = (int)(dval * 256.0f);
+ }
+ }
+ //synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, fbiastable[lf][distval],amplog);
+ }
+ }
+}
+
+// Per-note table initialisation follows
+
+static void initSaw(NoteLookup *noteLookup, Bit32s div) {
+ for (int rsaw = 0; rsaw <= 100; rsaw++) {
+ float fsaw;
+ if (rsaw < 50)
+ fsaw = 50.0f;
+ else
+ fsaw = (float)rsaw;
+ int tmpdiv = div << 17;
+
+ //(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(NoteLookup *noteLookup, float f) {
+ for (int dep = 0; dep < 5; dep++) {
+ if (dep == 0) {
+ noteLookup->fildepTable[dep] = 256;
+ noteLookup->timekeyTable[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;
+ noteLookup->fildepTable[dep] = (int)tempdep;
+
+ ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]);
+ noteLookup->timekeyTable[dep] = (int)(ff * 256.0f);
+ }
+ }
+ //synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]);
+}
+
+File *TableInitialiser::initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file) {
+ int iDiv = (int)div;
+ noteLookup->waveformSize[0] = iDiv << 2;
+ noteLookup->waveformSize[1] = iDiv << 2;
+ noteLookup->waveformSize[2] = iDiv << 3;
+ 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 / (div * 2.0);
+
+ for (int fa = 0; fa < (iDiv << 2); fa++) {
+ double sa = fa * sd;
+
+#if 0
+ //FIXME:KG: Credit Timo Strunk (bastardo on #scummvm) for help with this!
+ double saw = 0.5 * DOUBLE_PI - sa / 2;
+#else
+ double saw = 0.0;
+ for (int sinus = 1; sinus < div; sinus++) {
+ double fsinus = (double)sinus;
+ saw += sin(fsinus * sa) / fsinus;
+ }
+#endif
+
+ // This works pretty well
+ noteLookup->waveforms[0][fa] = (Bit16s)(saw * -ampsize / 2);
+ noteLookup->waveforms[1][fa] = (Bit16s)(cos(sa / 2.0) * -ampsize);
+ noteLookup->waveforms[2][fa * 2] = (Bit16s)(cos(sa - DOUBLE_PI) * -ampsize);
+ noteLookup->waveforms[2][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - DOUBLE_PI) * -ampsize);
+ }
+ }
+ 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 - 0);
+ if (tfadd < 0)
+ tfadd = 0;
+
+ float freqsum = expf((cfmult + tfadd) / 30.0f) / 4.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 *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry pcmWaves[128], File *file) {
+ float ampsize = WGAMP;
+ float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));
+ float div = rate / freq;
+ noteLookup->div = (int)div;
+
+ if (noteLookup->div == 0)
+ noteLookup->div = 1;
+
+ initSaw(noteLookup, noteLookup->div);
+ initDep(noteLookup, note);
+
+ //synth->printDebug("Note %f; freq=%f, div=%f", note, freq, rate / freq);
+ file = initWave(synth, noteLookup, ampsize, div, file);
+
+ // Create the pitch tables
+
+ double rateMult = 32000.0 / rate;
+ double tuner = freq * 65536.0f;
+ for (int pc = 0; pc < 128; pc++) {
+ noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);
+ }
+
+ initFiltTable(noteLookup, freq, rate);
+ initNFiltTable(noteLookup, freq, rate);
+ return file;
+}
+
+bool TableInitialiser::initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], 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, 3};
+ sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune);
+
+ File *file = NULL;
+ char header[20];
+ strncpy(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("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 1);
+ 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 TableInitialiser::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;
+ }
+ }
+ }
+ initialisedMasterTune = 0.0f;
+}
+
+bool TableInitialiser::initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune) {
+ if (sampleRate <= 0.0f) {
+ synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);
+ return false;
+ }
+ if (initialisedSampleRate == 0.0f) {
+ initMT32ConstantTables(synth);
+ }
+ if (initialisedSampleRate != sampleRate) {
+ initFiltCoeff(sampleRate);
+ initEnvelopes(sampleRate);
+ }
+ if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) {
+ freeNotes();
+ if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) {
+ return false;
+ }
+ initialisedSampleRate = sampleRate;
+ initialisedMasterTune = masterTune;
+ }
+ return true;
+}
+
+}
diff --git a/sound/softsynth/mt32/tables.h b/sound/softsynth/mt32/tables.h
new file mode 100644
index 0000000000..0c85796f4f
--- /dev/null
+++ b/sound/softsynth/mt32/tables.h
@@ -0,0 +1,101 @@
+/* Copyright (c) 2003-2004 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;
+
+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. Do 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
+
+// Amplitude of waveform generator
+const int WGAMP = 7168; // 8192?
+
+class Synth;
+
+extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT];
+
+// Some optimization stuff
+extern Bit32s keytable[217];
+extern Bit16s sintable[65536];
+extern Bit32u lfotable[101];
+extern Bit32s penvtable[16][101];
+extern Bit32s filveltable[128][101];
+extern Bit32s veltkeytable[5][128];
+extern Bit32s pulsetable[101];
+extern Bit32s ampbiastable[13][128];
+extern Bit32s fbiastable[15][128];
+extern float filtcoeff[FILTERGRAN][31][8];
+extern Bit32s finetable[201];
+extern Bit32u lfoptable[101][101];
+extern Bit32s ampveltable[128][64];
+extern Bit32s pwveltable[15][128];
+extern Bit32s envtimetable[101];
+extern Bit32s decaytimetable[101];
+extern Bit32s lasttimetable[101];
+extern Bit32s voltable[128];
+extern float ResonInv[31];
+
+struct NoteLookup {
+ Bit32u div;
+ Bit32u wavTable[128];
+ Bit32s sawTable[101];
+ Bit32s fildepTable[5];
+ Bit32s timekeyTable[5];
+ int filtTable[2][201];
+ int nfiltTable[101][101];
+ Bit16s *waveforms[3];
+ Bit32u waveformSize[3];
+};
+
+extern NoteLookup noteLookups[NUM_NOTES];
+
+class TableInitialiser {
+ static void initMT32ConstantTables(Synth *synth);
+ static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file);
+ static bool initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float tuning);
+public:
+ static bool initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune);
+ static File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry pcmWaves[128], File *file);
+ static void freeNotes();
+};
+
+}
+
+#endif
diff --git a/sound/softsynth/ym2612.cpp b/sound/softsynth/ym2612.cpp
new file mode 100644
index 0000000000..b9ad9b35ca
--- /dev/null
+++ b/sound/softsynth/ym2612.cpp
@@ -0,0 +1,907 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2004 The ScummVM project
+ *
+ * YM2612 tone generation code written by Tomoaki Hayasaka.
+ * Used under the terms of the GNU General Public License.
+ * Adpated to ScummVM by Jamieson Christian.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ */
+
+#include "sound/softsynth/emumidi.h"
+
+#include <math.h>
+
+
+////////////////////////////////////////
+//
+// Miscellaneous
+//
+////////////////////////////////////////
+
+static int *sintbl = 0;
+static int *powtbl = 0;
+static int *frequencyTable = 0;
+static int *keycodeTable = 0;
+static int *keyscaleTable = 0;
+static int *attackOut = 0;
+
+////////////////////////////////////////
+//
+// Class declarations
+//
+////////////////////////////////////////
+
+class Operator2612;
+class Voice2612;
+class MidiChannel_YM2612;
+class MidiDriver_YM2612;
+
+class Operator2612 {
+protected:
+ Voice2612 *_owner;
+ enum State { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
+ State _state;
+ int32 _currentLevel;
+ int _frequency;
+ uint32 _phase;
+ int _lastOutput;
+ int _feedbackLevel;
+ int _detune;
+ int _multiple;
+ int32 _totalLevel;
+ int _keyScale;
+ int _velocity;
+ int _specifiedTotalLevel;
+ int _specifiedAttackRate;
+ int _specifiedDecayRate;
+ int _specifiedSustainLevel;
+ int _specifiedSustainRate;
+ int _specifiedReleaseRate;
+ int _tickCount;
+ int _attackTime;
+ int32 _decayRate;
+ int32 _sustainLevel;
+ int32 _sustainRate;
+ int32 _releaseRate;
+
+public:
+ Operator2612 (Voice2612 *owner);
+ ~Operator2612();
+ void feedbackLevel(int level);
+ void setInstrument(byte const *instrument);
+ void velocity(int velo);
+ void keyOn();
+ void keyOff();
+ void frequency(int freq);
+ void nextTick(const int *phaseShift, int *outbuf, int buflen);
+ bool inUse() { return (_state != _s_ready); }
+};
+
+class Voice2612 {
+public:
+ Voice2612 *next;
+ uint16 _rate;
+
+protected:
+ Operator2612 *_opr[4];
+ int _velocity;
+ int _control7;
+ int _note;
+ int _frequencyOffs;
+ int _frequency;
+ int _algorithm;
+
+ int *_buffer;
+ int _buflen;
+
+public:
+ Voice2612();
+ ~Voice2612();
+ void setControlParameter(int control, int value);
+ void setInstrument(byte const *instrument);
+ void velocity(int velo);
+ void nextTick(int *outbuf, int buflen);
+ void noteOn(int n, int onVelo);
+ bool noteOff(int note);
+ void pitchBend(int value);
+ void recalculateFrequency();
+};
+
+class MidiChannel_YM2612 : public MidiChannel {
+protected:
+ uint16 _rate;
+ Voice2612 *_voices;
+ Voice2612 *_next_voice;
+
+public:
+ void removeAllVoices();
+ void nextTick(int *outbuf, int buflen);
+ void rate(uint16 r);
+
+public:
+ MidiChannel_YM2612();
+ virtual ~MidiChannel_YM2612();
+
+ // MidiChannel interface
+ MidiDriver *device() { return 0; }
+ byte getNumber() { return 0; }
+ void release() { }
+ void send(uint32 b) { }
+ void noteOff(byte note);
+ void noteOn(byte note, byte onVelo);
+ void programChange(byte program) { }
+ void pitchBend(int16 value);
+ void controlChange(byte control, byte value);
+ void pitchBendFactor(byte value) { }
+ void sysEx_customInstrument(uint32 type, byte *instr);
+};
+
+class MidiDriver_YM2612 : public MidiDriver_Emulated {
+protected:
+ MidiChannel_YM2612 *_channel[16];
+
+ int _next_voice;
+ int _volume;
+
+protected:
+ static void createLookupTables();
+ void nextTick(int16 *buf1, int buflen);
+ int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
+ void rate(uint16 r);
+
+ void generateSamples(int16 *buf, int len);
+
+public:
+ MidiDriver_YM2612(SoundMixer *mixer);
+ virtual ~MidiDriver_YM2612();
+
+ int open();
+ void close();
+ void send(uint32 b);
+ void send(byte channel, uint32 b); // Supports higher than channel 15
+ uint32 property(int prop, uint32 param) { return 0; }
+
+ void setPitchBendRange(byte channel, uint range) { }
+ void sysEx(byte *msg, uint16 length);
+
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+
+ // AudioStream API
+ bool isStereo() const { return true; }
+ int getRate() const { return _mixer->getOutputRate(); }
+};
+
+////////////////////////////////////////
+//
+// Operator2612 implementation
+//
+////////////////////////////////////////
+
+Operator2612::Operator2612 (Voice2612 *owner) :
+ _owner (owner),
+ _state (_s_ready),
+ _currentLevel ((int32)0x7f << 15),
+ _phase (0),
+ _lastOutput (0),
+ _feedbackLevel (0),
+ _detune (0),
+ _multiple (1),
+ _keyScale (0),
+ _specifiedTotalLevel (127),
+ _specifiedAttackRate (0),
+ _specifiedDecayRate (0),
+ _specifiedSustainRate (0),
+ _specifiedReleaseRate (15) {
+ velocity(0);
+}
+
+Operator2612::~Operator2612()
+{ }
+
+void Operator2612::velocity(int velo) {
+ _velocity = velo;
+ _totalLevel = ((int32)_specifiedTotalLevel << 15) +
+ ((int32)(127-_velocity) << 13);
+ _sustainLevel = ((int32)_specifiedSustainLevel << 17);
+}
+
+void Operator2612::feedbackLevel(int level) {
+ _feedbackLevel = level;
+}
+
+void Operator2612::setInstrument(byte const *instrument) {
+ _detune = (instrument[8] >> 4) & 7;
+ _multiple = instrument[8] & 15;
+ _specifiedTotalLevel = instrument[12] & 127;
+ _keyScale = (instrument[16] >> 6) & 3;
+ _specifiedAttackRate = instrument[16] & 31;
+ _specifiedDecayRate = instrument[20] & 31;
+ _specifiedSustainRate = instrument[24] & 31;
+ _specifiedSustainLevel = (instrument[28] >> 4) & 15;
+ _specifiedReleaseRate = instrument[28] & 15;
+ _state = _s_ready; // 本物ではどうなのかな?
+ velocity(_velocity);
+}
+
+void Operator2612::keyOn() {
+ _state = _s_attacking;
+ _tickCount = 0;
+ _phase = 0; // どうも、実際こうらしい
+ _currentLevel = ((int32)0x7f << 15); // これも、実際こうらしい
+}
+
+void Operator2612::keyOff() {
+ if (_state != _s_ready)
+ _state = _s_releasing;
+}
+
+void Operator2612::frequency(int freq) {
+ double value; // Use for intermediate computations to avoid int64 arithmetic
+ int r;
+
+ _frequency = freq / _owner->_rate;
+
+ r = _specifiedAttackRate;
+ if (r != 0) {
+ r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
+ if (r >= 64)
+ r = 63; // するべきなんだろうとは思うんだけど (赤p.207)
+ }
+
+ r = 63 - r;
+ if (_specifiedTotalLevel >= 128)
+ value = 0;
+ else {
+ value = powtbl[(r&3) << 7];
+ value *= 1 << (r >> 2);
+ value *= 41; // r == 20 のとき、0-96[db] が 10.01[ms] == 41.00096
+ value /= 1 << (15 + 5);
+ value *= 127 - _specifiedTotalLevel;
+ value /= 127;
+ }
+ _attackTime = (int32) value; // 1 秒 == (1 << 12)
+ if (_attackTime > 0)
+ _attackTime = (1 << (12+10)) / (_owner->_rate * _attackTime);
+
+ r = _specifiedDecayRate;
+ if (r != 0) {
+ r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
+ if (r >= 64)
+ r = 63;
+ }
+ value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
+ _decayRate = (int32) value / _owner->_rate;
+
+ r = _specifiedSustainRate;
+ if (r != 0) {
+ r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
+ if (r >= 64)
+ r = 63;
+ }
+ value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
+ _sustainRate = (int32) value / _owner->_rate;
+
+ r = _specifiedReleaseRate;
+ if (r != 0) {
+ r = r * 2 + 1; // (Translated) I cannot know whether the timing is a good choice or not
+ r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
+ // KS による補正はあるらしい。赤p.206 では記述されてないけど。
+ if (r >= 64)
+ r = 63;
+ }
+ value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
+ _releaseRate = (int32) value / _owner->_rate;
+}
+
+void Operator2612::nextTick(const int *phasebuf, int *outbuf, int buflen) {
+ if (_state == _s_ready)
+ return;
+ if (_state == _s_attacking && _attackTime <= 0) {
+ _currentLevel = 0;
+ _state = _s_decaying;
+ }
+
+ int32 levelIncrement = 0;
+ int32 target = 0;
+ State next_state = _s_ready;
+ const int32 zero_level = ((int32)0x7f << 15);
+ const int phaseIncrement = (_multiple > 0) ? (_frequency * _multiple) : (_frequency / 2);
+
+ int32 output = _lastOutput;
+ int32 level = _currentLevel + _totalLevel;
+
+ while (buflen) {
+ switch (_state) {
+ case _s_ready:
+ return;
+ break;
+ case _s_attacking:
+ next_state = _s_attacking;
+ break;
+ case _s_decaying:
+ levelIncrement = _decayRate;
+ target = _sustainLevel + _totalLevel;
+ next_state = _s_sustaining;
+ break;
+ case _s_sustaining:
+ levelIncrement = _sustainRate;
+ target = zero_level + _totalLevel;
+ next_state = _s_ready;
+ break;
+ case _s_releasing:
+ levelIncrement = _releaseRate;
+ target = zero_level + _totalLevel;
+ next_state = _s_ready;
+ break;
+ }
+
+ bool switching = false;
+ do {
+ if (next_state == _s_attacking) {
+ // Attack phase
+ ++_tickCount;
+ int i = (int) (_tickCount * _attackTime);
+ if (i >= 1024) {
+ level = _totalLevel;
+ _state = _s_decaying;
+ switching = true;
+ } else {
+ level = (attackOut[i] << (31 - 8 - 16)) + _totalLevel;
+ }
+ } else {
+ // Decay, Sustain and Release phases
+ level += levelIncrement;
+ if (level >= target) {
+ level = target;
+ _state = next_state;
+ switching = true;
+ }
+ }
+
+ if (level < zero_level) {
+ int phaseShift = *phasebuf >> 2; // 正しい変調量は? 3 じゃ小さすぎで 2 じゃ大きいような。
+ if (_feedbackLevel)
+ phaseShift += (output << (_feedbackLevel - 1)) / 1024;
+ output = sintbl[((_phase >> 7) + phaseShift) & 0x7ff];
+ output >>= (level >> 18); // 正しい減衰量は?
+ // Here is the original code, which requires 64-bit ints
+// output *= powtbl[511 - ((level>>25)&511)];
+// output >>= 16;
+// output >>= 1;
+ // And here's our 32-bit trick for doing it. (Props to Fingolfin!)
+ // Result varies from original code by max of 1.
+// int powVal = powtbl[511 - ((level>>9)&511)];
+// int outputHI = output / 256;
+// int powHI = powVal / 256;
+// output = (outputHI * powHI) / 2 + (outputHI * (powVal % 256) + powHI * (output % 256)) / 512;
+ // And here's the even faster code.
+ // Result varies from original code by max of 8.
+ output = ((output >> 4) * (powtbl[511-((level>>9)&511)] >> 3)) / 1024;
+
+ _phase += phaseIncrement;
+ _phase &= 0x3ffff;
+ } else
+ output = 0;
+
+ *outbuf += output;
+ --buflen;
+ ++phasebuf;
+ ++outbuf;
+ } while (buflen && !switching);
+ }
+ _lastOutput = output;
+ _currentLevel = level - _totalLevel;
+}
+
+////////////////////////////////////////
+//
+// Voice2612 implementation
+//
+////////////////////////////////////////
+
+Voice2612::Voice2612() {
+ next = 0;
+ _control7 = 127;
+ _note = 40;
+ _frequency = 440;
+ _frequencyOffs = 0x2000;
+ _algorithm = 7;
+
+ _buffer = 0;
+ _buflen = 0;
+
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); ++i)
+ _opr[i] = new Operator2612 (this);
+ velocity(0);
+}
+
+Voice2612::~Voice2612() {
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); ++i)
+ delete _opr[i];
+ free(_buffer);
+}
+
+void Voice2612::velocity(int velo) {
+ _velocity = velo;
+#if 0
+ int v = (velo * _control7) >> 7; // これだと精度良くないですね
+#else
+ int v = velo + (_control7 - 127) * 4;
+#endif
+ bool iscarrier[8][4] = {
+ { false, false, false, true, }, //0
+ { false, false, false, true, }, //1
+ { false, false, false, true, }, //2
+ { false, false, false, true, }, //3
+ { false, true, false, true, }, //4
+ { false, true, true, true, }, //5
+ { false, true, true, true, }, //6
+ { true, true, true, true, }, //7
+ };
+ int opr;
+ for (opr = 0; opr < 4; opr++)
+ if (iscarrier[_algorithm][opr])
+ _opr[opr]->velocity(v);
+ else
+ _opr[opr]->velocity(127);
+}
+
+void Voice2612::setControlParameter(int control, int value) {
+ switch (control) {
+ case 7:
+ _control7 = value;
+ velocity(_velocity);
+ break;
+ case 123:
+ // All notes off
+ noteOff(_note);
+ };
+}
+
+void Voice2612::setInstrument(byte const *instrument) {
+ if (instrument == NULL)
+ return;
+
+ _algorithm = instrument[32] & 7;
+ _opr[0]->feedbackLevel((instrument[32] >> 3) & 7);
+ _opr[1]->feedbackLevel(0);
+ _opr[2]->feedbackLevel(0);
+ _opr[3]->feedbackLevel(0);
+ _opr[0]->setInstrument(instrument + 0);
+ _opr[1]->setInstrument(instrument + 2);
+ _opr[2]->setInstrument(instrument + 1);
+ _opr[3]->setInstrument(instrument + 3);
+}
+
+void Voice2612::nextTick(int *outbuf, int buflen) {
+ if (_velocity == 0)
+ return;
+
+ if (_buflen < buflen) {
+ free(_buffer);
+ _buflen = buflen;
+ _buffer = (int *) malloc(sizeof(int) * buflen * 2);
+ }
+
+ int *buf1 = _buffer;
+ int *buf2 = _buffer + buflen;
+ memset(_buffer, 0, sizeof(int) * buflen * 2);
+
+ switch (_algorithm) {
+ case 0:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, buf1, buflen);
+ memset (buf2, 0, sizeof (int) * buflen);
+ _opr[2]->nextTick(buf1, buf2, buflen);
+ _opr[3]->nextTick(buf2, outbuf, buflen);
+ break;
+ case 1:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf1, buf2, buflen);
+ _opr[2]->nextTick(buf2, buf1, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 2:
+ _opr[1]->nextTick(buf1, buf2, buflen);
+ _opr[2]->nextTick(buf2, buf1, buflen);
+ memset(buf2, 0, sizeof(int) * buflen);
+ _opr[0]->nextTick(buf2, buf1, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 3:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, buf1, buflen);
+ memset(buf2, 0, sizeof(int) * buflen);
+ _opr[2]->nextTick(buf2, buf1, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 4:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, outbuf, buflen);
+ _opr[2]->nextTick(buf1, buf1, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 5:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, outbuf, buflen);
+ _opr[2]->nextTick(buf2, outbuf, buflen);
+ _opr[3]->nextTick(buf2, outbuf, buflen);
+ break;
+ case 6:
+ _opr[0]->nextTick(buf1, buf2, buflen);
+ _opr[1]->nextTick(buf2, outbuf, buflen);
+ _opr[2]->nextTick(buf1, outbuf, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ case 7:
+ _opr[0]->nextTick(buf1, outbuf, buflen);
+ _opr[1]->nextTick(buf1, outbuf, buflen);
+ _opr[2]->nextTick(buf1, outbuf, buflen);
+ _opr[3]->nextTick(buf1, outbuf, buflen);
+ break;
+ };
+}
+
+void Voice2612::noteOn(int n, int onVelo) {
+ _note = n;
+ velocity(onVelo);
+ recalculateFrequency();
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); i++)
+ _opr[i]->keyOn();
+}
+
+bool Voice2612::noteOff(int note) {
+ if (_note != note)
+ return false;
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); i++)
+ _opr[i]->keyOff();
+ return true;
+}
+
+void Voice2612::pitchBend(int value) {
+ _frequencyOffs = value;
+ recalculateFrequency();
+}
+
+void Voice2612::recalculateFrequency() {
+ // MIDI とも違うし....
+ // どういう仕様なんだろうか?
+ // と思ったら、なんと、これ (↓) が正解らしい。
+ int32 basefreq = frequencyTable[_note];
+ int cfreq = frequencyTable[_note - (_note % 12)];
+ int oct = _note / 12;
+ int fnum = (int) (((double)basefreq * (1 << 13)) / cfreq); // OPL の fnum と同じようなもの。
+ fnum += _frequencyOffs - 0x2000;
+ if (fnum < 0x2000) {
+ fnum += 0x2000;
+ oct--;
+ }
+ if (fnum >= 0x4000) {
+ fnum -= 0x2000;
+ oct++;
+ }
+
+ // _frequency は最終的にバイアス 256*1024 倍
+ _frequency = (int) ((frequencyTable[oct*12] * (double)fnum) / 8);
+
+ int i;
+ for (i = 0; i < ARRAYSIZE(_opr); i++)
+ _opr[i]->frequency(_frequency);
+}
+
+////////////////////////////////////////
+//
+// MidiChannel_YM2612
+//
+////////////////////////////////////////
+
+MidiChannel_YM2612::MidiChannel_YM2612() {
+ _voices = 0;
+ _next_voice = 0;
+}
+
+MidiChannel_YM2612::~MidiChannel_YM2612() {
+ removeAllVoices();
+}
+
+void MidiChannel_YM2612::removeAllVoices() {
+ if (!_voices)
+ return;
+ Voice2612 *last, *voice = _voices;
+ for (; voice; voice = last) {
+ last = voice->next;
+ delete voice;
+ }
+ _voices = _next_voice = 0;
+}
+
+void MidiChannel_YM2612::noteOn(byte note, byte onVelo) {
+ if (!_voices)
+ return;
+ _next_voice = _next_voice ? _next_voice : _voices;
+ _next_voice->noteOn(note, onVelo);
+ _next_voice = _next_voice->next;
+}
+
+void MidiChannel_YM2612::noteOff(byte note) {
+ if (!_voices)
+ return;
+ if (_next_voice == _voices)
+ _next_voice = 0;
+ Voice2612 *voice = _next_voice;
+ do {
+ if (!voice)
+ voice = _voices;
+ if (voice->noteOff(note)) {
+ _next_voice = voice;
+ break;
+ }
+ voice = voice->next;
+ } while (voice != _next_voice);
+}
+
+void MidiChannel_YM2612::controlChange(byte control, byte value) {
+ // いいのかこれで?
+ if (control == 121) {
+ // Reset controller
+ removeAllVoices();
+ } else {
+ Voice2612 *voice = _voices;
+ for (; voice; voice = voice->next)
+ voice->setControlParameter(control, value);
+ }
+}
+
+void MidiChannel_YM2612::sysEx_customInstrument(uint32 type, byte *fmInst) {
+ if (type != 'EUP ')
+ return;
+ Voice2612 *voice = new Voice2612;
+ voice->next = _voices;
+ _voices = voice;
+ voice->_rate = _rate;
+ voice->setInstrument(fmInst);
+}
+
+void MidiChannel_YM2612::pitchBend(int16 value) {
+ // いいのかこれで?
+ Voice2612 *voice = _voices;
+ for (; voice; voice = voice->next)
+ voice->pitchBend(value);
+}
+
+void MidiChannel_YM2612::nextTick(int *outbuf, int buflen) {
+ Voice2612 *voice = _voices;
+ for (; voice; voice = voice->next)
+ voice->nextTick(outbuf, buflen);
+}
+
+void MidiChannel_YM2612::rate(uint16 r) {
+ _rate = r;
+ Voice2612 *voice = _voices;
+ for (; voice; voice = voice->next)
+ voice->_rate = r;
+}
+
+////////////////////////////////////////
+//
+// MidiDriver_YM2612
+//
+////////////////////////////////////////
+
+MidiDriver_YM2612::MidiDriver_YM2612(SoundMixer *mixer)
+ : MidiDriver_Emulated(mixer) {
+ _next_voice = 0;
+
+ createLookupTables();
+ _volume = 256;
+ int i;
+ for (i = 0; i < ARRAYSIZE(_channel); i++)
+ _channel[i] = new MidiChannel_YM2612;
+ rate(getRate());
+}
+
+MidiDriver_YM2612::~MidiDriver_YM2612() {
+ int i;
+ for (i = 0; i < ARRAYSIZE(_channel); i++)
+ delete _channel[i];
+ delete sintbl;
+ delete powtbl;
+ delete frequencyTable;
+ delete keycodeTable;
+ delete keyscaleTable;
+ delete attackOut;
+ sintbl = powtbl = frequencyTable = keycodeTable = keyscaleTable = attackOut = 0;
+}
+
+int MidiDriver_YM2612::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ MidiDriver_Emulated::open();
+
+ _mixer->setupPremix(this);
+ return 0;
+}
+
+void MidiDriver_YM2612::close() {
+ if (!_isOpen)
+ return;
+ _isOpen = false;
+
+ // Detach the premix callback handler
+ _mixer->setupPremix(0);
+}
+
+void MidiDriver_YM2612::send(uint32 b) {
+ send(b & 0xF, b & 0xFFFFFFF0);
+}
+
+void MidiDriver_YM2612::send(byte chan, uint32 b) {
+ //byte param3 = (byte) ((b >> 24) & 0xFF);
+ byte param2 = (byte) ((b >> 16) & 0xFF);
+ byte param1 = (byte) ((b >> 8) & 0xFF);
+ byte cmd = (byte) (b & 0xF0);
+ if (chan > ARRAYSIZE(_channel))
+ return;
+
+ switch (cmd) {
+ case 0x80:// Note Off
+ _channel[chan]->noteOff(param1);
+ break;
+ case 0x90: // Note On
+ _channel[chan]->noteOn(param1, param2);
+ break;
+ case 0xA0: // Aftertouch
+ break; // Not supported.
+ case 0xB0: // Control Change
+ _channel[chan]->controlChange(param1, param2);
+ break;
+ case 0xC0: // Program Change
+ _channel[chan]->programChange(param1);
+ break;
+ case 0xD0: // Channel Pressure
+ break; // Not supported.
+ case 0xE0: // Pitch Bend
+ _channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000);
+ break;
+ case 0xF0: // SysEx
+ // We should never get here! SysEx information has to be
+ // sent via high-level semantic methods.
+ warning("MidiDriver_YM2612: Receiving SysEx command on a send() call");
+ break;
+
+ default:
+ warning("MidiDriver_YM2612: Unknown send() command 0x%02X", cmd);
+ }
+}
+
+void MidiDriver_YM2612::sysEx(byte *msg, uint16 length) {
+ if (msg[0] != 0x7C || msg[1] >= ARRAYSIZE(_channel))
+ return;
+ _channel[msg[1]]->sysEx_customInstrument('EUP ', &msg[2]);
+}
+
+void MidiDriver_YM2612::generateSamples(int16 *data, int len) {
+ memset(data, 0, 2 * sizeof(int16) * len);
+ nextTick(data, len);
+}
+
+void MidiDriver_YM2612::nextTick(int16 *buf1, int buflen) {
+ int *buf0 = (int *)buf1;
+
+ int i;
+ for (i = 0; i < ARRAYSIZE(_channel); i++)
+ _channel[i]->nextTick(buf0, buflen);
+
+ for (i = 0; i < buflen; ++i)
+ buf1[i*2+1] = buf1[i*2] = ((buf0[i] * volume()) >> 10) & 0xffff;
+}
+
+void MidiDriver_YM2612::rate(uint16 r)
+{
+ int i;
+ for (i = 0; i < ARRAYSIZE(_channel); i++)
+ _channel[i]->rate(r);
+}
+
+void MidiDriver_YM2612::createLookupTables() {
+ {
+ int i;
+ sintbl = new int [2048];
+ for (i = 0; i < 2048; i++)
+ sintbl[i] = (int)(0xffff * sin(i/2048.0*2.0*PI));
+ }
+
+ {
+ int i;
+ powtbl = new int [1025];
+ for (i = 0; i <= 1024; i++)
+ powtbl[i] = (int)(0x10000 * pow(2.0, (i-512)/512.0));
+ }
+
+ {
+ int i;
+ int block;
+
+ static int fnum[] = {
+ 0x026a, 0x028f, 0x02b6, 0x02df,
+ 0x030b, 0x0339, 0x036a, 0x039e,
+ 0x03d5, 0x0410, 0x044e, 0x048f,
+ };
+
+ // (int)(880.0 * 256.0 * pow(2.0, (note-0x51)/12.0)); // バイアス 256 倍
+ // 0x45 が 440Hz (a4)、0x51 が 880Hz (a5) らしい
+ frequencyTable = new int [120];
+ for (block = -1; block < 9; block++) {
+ for (i = 0; i < 12; i++) {
+ double freq = fnum[i] * (166400.0 / 3) * pow(2.0, block-21);
+ frequencyTable[(block+1)*12+i] = (int)(256.0 * freq);
+ }
+ }
+
+ keycodeTable = new int [120];
+ // detune 量の計算や KS による rate 変換に使うんじゃないかな
+ for (block = -1; block < 9; block++) {
+ for (i = 0; i < 12; i++) {
+ // see p.204
+ int f8 = (fnum[i] >> 7) & 1;
+ int f9 = (fnum[i] >> 8) & 1;
+ int f10 = (fnum[i] >> 9) & 1;
+ int f11 = (fnum[i] >> 10) & 1;
+ int n4 = f11;
+ int n3 = f11&(f10|f9|f8) | (~f11&f10&f9&f8);
+ int note = n4*2 + n3;
+ // see p.207
+ keycodeTable[(block+1)*12+i] = block*4 + note;
+ }
+ }
+ }
+
+ {
+ int freq;
+ keyscaleTable = new int [8192];
+ keyscaleTable[0] = 0;
+ for (freq = 1; freq < 8192; freq++) {
+ keyscaleTable[freq] = (int)(log((double)freq) / 9.03 * 32.0) - 1;
+ // 8368[Hz] (o9c) で 32くらい。9.03 =:= ln 8368
+ }
+ }
+
+ {
+ int i;
+ attackOut = new int [1024];
+ for (i = 0; i < 1024; i++)
+ attackOut[i] = (int)(((0x7fff+0x03a5)*30.0) / (30.0+i)) - 0x03a5;
+ }
+}
+
+////////////////////////////////////////
+//
+// MidiDriver_YM2612 factory
+//
+////////////////////////////////////////
+
+MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer) {
+ return new MidiDriver_YM2612(mixer);
+}