aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.common3
-rw-r--r--backends/midi/mt32.cpp243
-rw-r--r--backends/midi/mt32/file.cpp66
-rw-r--r--backends/midi/mt32/file.h62
-rw-r--r--backends/midi/mt32/i386.cpp817
-rw-r--r--backends/midi/mt32/i386.h49
-rw-r--r--backends/midi/mt32/module.mk18
-rw-r--r--backends/midi/mt32/mt32.cpp156
-rw-r--r--backends/midi/mt32/mt32emu.h36
-rw-r--r--backends/midi/mt32/part.cpp595
-rw-r--r--backends/midi/mt32/part.h90
-rw-r--r--backends/midi/mt32/partial.cpp1231
-rw-r--r--backends/midi/mt32/partial.h183
-rw-r--r--backends/midi/mt32/partialManager.cpp269
-rw-r--r--backends/midi/mt32/partialManager.h56
-rw-r--r--backends/midi/mt32/structures.h737
-rw-r--r--backends/midi/mt32/synth.cpp5053
-rw-r--r--backends/midi/mt32/synth.h252
-rw-r--r--backends/midi/mt32/tables.cpp727
-rw-r--r--backends/midi/mt32/tables.h97
-rw-r--r--backends/module.mk9
-rw-r--r--base/gameDetector.cpp6
-rwxr-xr-xconfigure21
-rw-r--r--dists/msvc7/scummvm.vcproj60
-rw-r--r--dists/msvc8/scummvm.vcproj82
-rw-r--r--scumm/scumm.cpp3
-rw-r--r--sound/mididrv.cpp2
-rw-r--r--sound/mididrv.h4
28 files changed, 5357 insertions, 5570 deletions
diff --git a/Makefile.common b/Makefile.common
index bb2d5383e8..bd37fb6943 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -89,6 +89,9 @@ MODULES += \
sound \
common
+ifdef USE_MT32EMU
+MODULES += backends/midi/mt32
+endif
######################################################################
# The build rules follow - normally you should have no need to
diff --git a/backends/midi/mt32.cpp b/backends/midi/mt32.cpp
new file mode 100644
index 0000000000..2931e94c71
--- /dev/null
+++ b/backends/midi/mt32.cpp
@@ -0,0 +1,243 @@
+/* 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 "stdafx.h"
+#include "common/scummsys.h"
+
+#ifdef USE_MT32EMU
+
+#include "backends/midi/mt32/mt32emu.h"
+
+#include "backends/midi/emumidi.h"
+#include "sound/mpu401.h"
+
+#include "common/util.h"
+#include "common/file.h"
+#include "common/config-manager.h"
+
+class MidiDriver_MT32 : public MidiDriver_Emulated {
+private:
+ MidiChannel_MPU401 _midi_channels[16];
+ uint16 _channel_mask;
+ MT32Emu::Synth *_synth;
+
+ int _outputRate;
+
+protected:
+ void generate_samples(int16 *buf, int len);
+
+public:
+ MidiDriver_MT32(SoundMixer *mixer);
+ virtual ~MidiDriver_MT32();
+
+ int open();
+ void close();
+ void send(uint32 b);
+ 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 *ptr, size_t size) {
+ return file.read(ptr, size);
+ }
+ bool readLine(char *ptr, size_t size) {
+ return file.gets(ptr, size) != NULL;
+ }
+ size_t write(const void *ptr, size_t size) {
+ return file.write(ptr, size);
+ }
+ int readByte() {
+ byte b = file.readByte();
+ if (file.eof())
+ return -1;
+ return b;
+ }
+ bool writeByte(unsigned char out) {
+ file.writeByte(out);
+ if (file.ioFailed())
+ return false;
+ return true;
+ }
+ bool isEOF() {
+ return file.eof();
+ }
+};
+
+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;
+}
+
+////////////////////////////////////////
+//
+// MidiDriver_MT32
+//
+////////////////////////////////////////
+
+void report(int type, ...) {}
+
+MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer) : MidiDriver_Emulated(mixer) {
+ _channel_mask = 0xFFFF; // Permit all 16 channels by default
+ uint i;
+ for (i = 0; i < ARRAYSIZE(_midi_channels); ++i) {
+ _midi_channels [i].init (this, i);
+ }
+ _synth = NULL;
+
+ _baseFreq = 1000;
+
+ _outputRate = 44100;
+}
+
+MidiDriver_MT32::~MidiDriver_MT32() {
+ if (_synth != NULL)
+ delete _synth;
+}
+
+static void vdebug(void *data, const char *fmt, va_list list) {
+ // do nothing here now
+}
+
+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.UseDefault = false;
+ prop.RevType = 0;
+ prop.RevTime = 5;
+ prop.RevLevel = 3;
+ prop.userData = (void *)1;
+ prop.printDebug = &vdebug;
+ prop.openFile = MT32_OpenFile;
+ _synth = new MT32Emu::Synth();
+ if (!_synth->open(prop))
+ return MERR_DEVICE_NOT_AVAILABLE;
+
+ _mixer->setupPremix(this);
+
+ return 0;
+}
+
+void MidiDriver_MT32::send(uint32 b) {
+ _synth->playMsg(b);
+}
+
+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 premix callback handler
+ _mixer->setupPremix(0);
+
+ _synth->close();
+ delete _synth;
+ _synth = NULL;
+}
+
+void MidiDriver_MT32::generate_samples(int16 *data, int len) {
+ _synth->render(data, len);
+}
+
+uint32 MidiDriver_MT32::property (int prop, uint32 param) {
+ switch (prop) {
+ case PROP_CHANNEL_MASK:
+ _channel_mask = param & 0xFFFF;
+ return 1;
+ }
+
+ return 0;
+}
+
+MidiChannel *MidiDriver_MT32::allocateChannel() {
+ MidiChannel_MPU401 *chan;
+ uint i;
+
+ for (i = 0; i < ARRAYSIZE(_midi_channels); ++i) {
+ if (i == 9 || !(_channel_mask & (1 << i)))
+ continue;
+ chan = &_midi_channels[i];
+ if (chan->allocate()) {
+ return chan;
+ }
+ }
+ return NULL;
+}
+
+MidiChannel *MidiDriver_MT32::getPercussionChannel() {
+ return &_midi_channels [9];
+}
+
+////////////////////////////////////////
+//
+// 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/backends/midi/mt32/file.cpp b/backends/midi/mt32/file.cpp
new file mode 100644
index 0000000000..8660e8314c
--- /dev/null
+++ b/backends/midi/mt32/file.cpp
@@ -0,0 +1,66 @@
+/* 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 "file.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);
+ }
+
+ int ANSIFile::readByte() {
+ return fgetc(fp);
+ }
+
+ size_t ANSIFile::read(void *ptr, size_t size) {
+ return fread(ptr, 1, size, fp);
+ }
+
+ bool ANSIFile::readLine(char *ptr, size_t size) {
+ return fgets(ptr, (int)size, fp) != NULL;
+ }
+
+ bool ANSIFile::writeByte(unsigned char out) {
+ return fputc(out, fp) != EOF;
+ }
+
+ size_t ANSIFile::write(const void *ptr, size_t size) {
+ return fwrite(ptr, 1, size, fp);
+ }
+
+ bool ANSIFile::isEOF() {
+ return feof(fp) != 0;
+ }
+}
diff --git a/backends/midi/mt32/file.h b/backends/midi/mt32/file.h
new file mode 100644
index 0000000000..6c02712596
--- /dev/null
+++ b/backends/midi/mt32/file.h
@@ -0,0 +1,62 @@
+/* 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 *ptr, size_t size) = 0;
+ virtual bool readLine(char *ptr, size_t size) = 0;
+ virtual size_t write(const void *ptr, size_t size) = 0;
+ // Returns -1 in case of EOF or error
+ virtual int readByte() = 0;
+ virtual bool writeByte(unsigned char out) = 0;
+ 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 *ptr, size_t size);
+ bool readLine(char *ptr, size_t size);
+ size_t write(const void *, size_t size);
+ int readByte();
+ bool writeByte(unsigned char out);
+ bool isEOF();
+};
+
+}
+
+#endif
diff --git a/backends/midi/mt32/i386.cpp b/backends/midi/mt32/i386.cpp
new file mode 100644
index 0000000000..9bc5bcb4c5
--- /dev/null
+++ b/backends/midi/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 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 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/backends/midi/mt32/i386.h b/backends/midi/mt32/i386.h
new file mode 100644
index 0000000000..544a7bf6da
--- /dev/null
+++ b/backends/midi/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 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 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/backends/midi/mt32/module.mk b/backends/midi/mt32/module.mk
new file mode 100644
index 0000000000..e5739d90ee
--- /dev/null
+++ b/backends/midi/mt32/module.mk
@@ -0,0 +1,18 @@
+MODULE := backends/midi/mt32
+
+MODULE_OBJS := \
+ backends/midi/mt32/file.o \
+ backends/midi/mt32/i386.o \
+ backends/midi/mt32/part.o \
+ backends/midi/mt32/partial.o \
+ backends/midi/mt32/partialManager.o \
+ backends/midi/mt32/synth.o \
+ backends/midi/mt32/tables.o \
+ backends/midi/mt32/freeverb.o
+
+
+MODULE_DIRS += \
+ backends/midi/mt32
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/backends/midi/mt32/mt32.cpp b/backends/midi/mt32/mt32.cpp
deleted file mode 100644
index 9faaa923ad..0000000000
--- a/backends/midi/mt32/mt32.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/* 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 "backends/midi/emumidi.h"
-
-#include "common/util.h"
-#include "common/file.h"
-
-#include "backends/midi/mt32/synth.h"
-
-class MidiDriver_MT32 : public MidiDriver_Emulated {
-private:
- CSynthMT32 *_synth;
-
- int _outputRate;
-
-protected:
- void generate_samples(int16 *buf, int len);
-
-public:
- MidiDriver_MT32(SoundMixer *mixer, const char *path);
- virtual ~MidiDriver_MT32();
-
- int open();
- void close();
- void send(uint32 b);
- 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 _outputRate; }
-};
-
-
-////////////////////////////////////////
-//
-// MidiDriver_MT32
-//
-////////////////////////////////////////
-
-
-MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer, const char *path)
- : MidiDriver_Emulated(mixer) {
- File fp;
-
- _synth = new CSynthMT32();
- File::addDefaultDirectory(path);
-
- _baseFreq = 1000;
-
- fp.open("waveforms.raw");
-
- if(!fp.isOpen()) {
- error("Unable to open waveforms.raw");
- }
-
- switch(fp.size()) {
- case 1410000:
- _outputRate = 32000;
- break;
- case 1944040:
- _outputRate = 44100;
- break;
- default:
- error("MT-32: Unknown waveforms.raw file sample rate");
- }
- fp.close();
-}
-
-MidiDriver_MT32::~MidiDriver_MT32() {
- delete _synth;
-}
-
-int MidiDriver_MT32::open() {
- SynthProperties prop;
-
- if (_isOpen)
- return MERR_ALREADY_OPEN;
-
- MidiDriver_Emulated::open();
-
- prop.SampleRate = getRate(); // 32000;
- prop.UseReverb = true;
- prop.UseDefault = true;
- //prop.RevType = 0;
- //prop.RevTime = 5;
- //prop.RevLevel = 3;
-
- _synth->ClassicOpen(prop);
-
- _mixer->setupPremix(this);
-
- return 0;
-}
-
-void MidiDriver_MT32::send(uint32 b) {
- _synth->PlayMsg(b);
-}
-
-void MidiDriver_MT32::sysEx(byte *msg, uint16 length) {
- _synth->PlaySysex(msg, length);
-}
-
-void MidiDriver_MT32::close() {
- if (!_isOpen)
- return;
- _isOpen = false;
-
- // Detach the premix callback handler
- _mixer->setupPremix(0);
-
- _synth->Close();
-}
-
-void MidiDriver_MT32::generate_samples(int16 *data, int len) {
- _synth->MT32_CallBack((uint8 *)data, len, _mixer->getMusicVolume());
-}
-
-
-////////////////////////////////////////
-//
-// MidiDriver_MT32 factory
-//
-////////////////////////////////////////
-
-MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer, const char *path) {
- return new MidiDriver_MT32(mixer, path);
-}
diff --git a/backends/midi/mt32/mt32emu.h b/backends/midi/mt32/mt32emu.h
new file mode 100644
index 0000000000..739be7de59
--- /dev/null
+++ b/backends/midi/mt32/mt32emu.h
@@ -0,0 +1,36 @@
+/* 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
+
+#include "freeverb.h"
+
+#include "structures.h"
+#include "i386.h"
+#include "file.h"
+#include "partial.h"
+#include "partialManager.h"
+#include "part.h"
+#include "tables.h"
+#include "synth.h"
+
+#endif
diff --git a/backends/midi/mt32/part.cpp b/backends/midi/mt32/part.cpp
new file mode 100644
index 0000000000..cfb40f3d50
--- /dev/null
+++ b/backends/midi/mt32/part.cpp
@@ -0,0 +1,595 @@
+/* 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"
+
+// Debugging stuff
+// Shows the instruments played
+#define DISPLAYINSTR 1
+
+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 Bit32u drumBend = 0x1000;
+
+// This caches the timbres/settings in use by the rhythm part
+static PatchCache drumCache[94][4];
+
+static volset drumPan[64];
+
+//FIXME:KG: Put this dpoly stuff somewhere better
+bool dpoly::isActive() {
+ return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL;
+}
+
+Bit64s dpoly::getAge() {
+ for (int i = 0; i < 4; i++) {
+ if (partials[i] != NULL) {
+ return partials[i]->age;
+ }
+ }
+ return 0;
+}
+
+Part::Part(Synth *useSynth, int usePartNum) {
+ this->synth = useSynth;
+ this->partNum = usePartNum;
+ isRhythm = (usePartNum == 8);
+ holdpedal = false;
+ if (isRhythm) {
+ strcpy(name, "Rhythm");
+ patchTemp = NULL;
+ timbreTemp = NULL;
+ rhythmTemp = &synth->mt32ram.params.rhythmSettings[0];
+ } else {
+ sprintf(name, "Part %d", partNum + 1);
+ patchTemp = &synth->mt32ram.params.patchSettings[partNum];
+ timbreTemp = &synth->mt32ram.params.timbreSettings[partNum];
+ rhythmTemp = NULL;
+ }
+ currentInstr[0] = 0;
+ volume = 102;
+ volumesetting.leftvol = 32767;
+ volumesetting.rightvol = 32767;
+ bend = 0x1000;
+ memset(polyTable,0,sizeof(polyTable));
+ memset(patchCache, 0, sizeof(patchCache));
+
+ if (isRhythm) {
+ init = true;
+ RefreshDrumCache();
+ }
+ init = false;
+}
+
+void Part::SetHoldPedal(bool pedalval) {
+ if (holdpedal && !pedalval)
+ StopPedalHold();
+ holdpedal = pedalval;
+}
+
+void Part::SetBend(int vol) {
+ if (isRhythm) {
+ synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, vol);
+ return;
+ }
+ //int tmpbend = ((vol - 0x2000) * (int)patchTemp->patch.benderRange) >> 13;
+ //bend = bendtable[tmpbend+24];
+
+ float bend_range = (float)patchTemp->patch.benderRange / 24;
+ bend = (Bit32u)(4096 + (vol - 8192) * bend_range);
+}
+
+void Part::SetModulation(int vol) {
+ if (isRhythm) {
+ synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, vol);
+ return;
+ }
+ // 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 * vol) >> 7;
+ //patchCache[t].lfoperiod = lfotable[newrate];
+ patchCache[t].lfodepth = newrate;
+ //FIXME:KG: timbreTemp->partial[t].lfo.depth =
+ }
+ }
+}
+
+void Part::RefreshDrumCache() {
+ if (!isRhythm) {
+ synth->printDebug("ERROR: RefreshDrumCache() called on non-rhythm part");
+ }
+ // Cache drum patches
+ for (int m = 0; m < 64; m++) {
+ int drumTimbre = rhythmTemp[m].timbre;
+ if (drumTimbre >= 94)
+ continue;
+ SetPatch(drumTimbre + 128); // This is to cache all the mapped drum timbres ahead of time
+ Bit16s pan = rhythmTemp[m].panpot; // They use R-L 0-14...
+ // FIXME:KG: If I don't have left/right mixed up here, it's pure luck
+ if (pan < 7) {
+ drumPan[m].leftvol = 32767;
+ drumPan[m].rightvol = pan * 4681;
+ } else {
+ drumPan[m].rightvol = 32767;
+ drumPan[m].leftvol = (14 - pan) * 4681;
+ }
+ }
+}
+
+int Part::FixBiaslevel(int srcpnt, int *dir) {
+ int noteat = srcpnt & 63;
+ int outnote;
+ *dir = 1;
+ if (srcpnt < 64)
+ *dir = 0;
+ outnote = 33 + noteat;
+ //synth->printDebug("Bias note %d, dir %d", outnote, *dir);
+
+ return outnote;
+}
+
+int Part::FixKeyfollow(int srckey, int *dir) {
+ if (srckey>=0 && srckey<=16) {
+ //int keyfix[17] = { 256, 128, 64, 0, 32, 64, 96, 128, 128+32, 192, 192+32, 256, 256+64, 256+128, 512, 259, 269 };
+ 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};
+
+ if (srckey<3)
+ *dir = -1;
+ else if (srckey==3)
+ *dir = 0;
+ else
+ *dir = 1;
+
+ return keyfix[srckey];
+ } else {
+ //LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey);
+ return 256;
+ }
+}
+
+void Part::RefreshPatch() {
+ SetPatch(-1);
+}
+
+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(PatchParam *patch) {
+ patchTemp->patch = *patch;
+}
+
+void Part::setTimbre(TimbreParam *timbre) {
+ *timbreTemp = *timbre;
+}
+
+unsigned int Part::getAbsTimbreNum() {
+ if (isRhythm) {
+ synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm");
+ return 0;
+ }
+ return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
+}
+
+void Part::SetPatch(int patchNum) {
+ int pcm;
+ int absTimbreNum = -1; // Initialised to please compiler
+ TimbreParam timSrc;
+ if (isRhythm) {
+ // "patchNum" is treated as "timbreNum" for rhythm part
+ if (patchNum < 128) {
+ synth->printDebug("%s: Patch #%d is not valid for rhythm (must be >= 128)", name, patchNum);
+ return;
+ }
+ absTimbreNum = patchNum;
+ timSrc = synth->mt32ram.params.timbres[absTimbreNum].timbre;
+ } else {
+ if (patchNum >= 0) {
+ setPatch(&synth->mt32ram.params.patches[patchNum]);
+ }
+ if (patchNum >= 0) {
+ setTimbre(&synth->mt32ram.params.timbres[getAbsTimbreNum()].timbre);
+ }
+ timSrc = *timbreTemp;
+#if 0
+ // Immediately stop all partials on this part (this is apparently *not* the correct behaviour)
+ for (int m = 0; m < MAXPOLY; m++) {
+ AbortPoly(poly);
+ }
+#else
+ // check if any partials are still playing on this part
+ // 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.
+ // Hopefully this is fairly rare.
+ for (int m = 0; m < MAXPOLY; m++) {
+ for (int i = 0; i < 4; i++) {
+ Partial *partial = polyTable[m].partials[i];
+ if (partial != NULL) {
+ // copy cache data
+ partial->cachebackup = patchCache[i];
+ // update pointers
+ partial->patchCache = &partial->cachebackup;
+ }
+ }
+ }
+#endif
+ }
+
+ memcpy(currentInstr, timSrc.common.name, 10);
+ currentInstr[10] = 0;
+
+ int partialCount = 0;
+ for (int t=0;t<4;t++) {
+ if ( ((timSrc.common.pmute >> (t)) & 0x1) == 1 ) {
+ patchCache[t].playPartial = true;
+ partialCount++;
+ } else {
+ patchCache[t].playPartial = false;
+ continue;
+ }
+
+ // Calculate and cache common parameters
+
+ pcm = timSrc.partial[t].wg.pcmwave;
+
+ patchCache[t].pcm = timSrc.partial[t].wg.pcmwave;
+ patchCache[t].useBender = (timSrc.partial[t].wg.bender == 1);
+
+ switch (t) {
+ case 0:
+ patchCache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct12] & 0x2) ? true : false;
+ patchCache[t].mix = PartialMixStruct[(int)timSrc.common.pstruct12];
+ patchCache[t].structurePosition = 0;
+ patchCache[t].structurePair = 1;
+ break;
+ case 1:
+ patchCache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct12] & 0x1) ? true : false;
+ patchCache[t].mix = PartialMixStruct[(int)timSrc.common.pstruct12];
+ patchCache[t].structurePosition = 1;
+ patchCache[t].structurePair = 0;
+ break;
+ case 2:
+ patchCache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct34] & 0x2) ? true : false;
+ patchCache[t].mix = PartialMixStruct[(int)timSrc.common.pstruct34];
+ patchCache[t].structurePosition = 0;
+ patchCache[t].structurePair = 3;
+ break;
+ case 3:
+ patchCache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct34] & 0x1) ? true : false;
+ patchCache[t].mix = PartialMixStruct[(int)timSrc.common.pstruct34];
+ patchCache[t].structurePosition = 1;
+ patchCache[t].structurePair = 2;
+ break;
+ default:
+ break;
+ }
+
+ patchCache[t].waveform = timSrc.partial[t].wg.waveform;
+ patchCache[t].pulsewidth = timSrc.partial[t].wg.pulsewid;
+ patchCache[t].pwsens = timSrc.partial[t].wg.pwvelo;
+ patchCache[t].pitchkeyfollow = FixKeyfollow(timSrc.partial[t].wg.keyfollow, &patchCache[t].pitchkeydir);
+
+ // Calculate and cache pitch stuff
+ patchCache[t].pitchshift = timSrc.partial[t].wg.coarse;
+ Bit32s pFine, fShift;
+ pFine = (Bit32s)timSrc.partial[t].wg.fine;
+ if (isRhythm) {
+ patchCache[t].pitchshift += 24;
+ fShift = pFine + 50;
+ } else {
+ patchCache[t].pitchshift += patchTemp->patch.keyShift;
+ fShift = pFine + (Bit32s)patchTemp->patch.fineTune;
+ }
+ patchCache[t].fineshift = finetable[fShift];
+
+ patchCache[t].pitchEnv = timSrc.partial[t].env;
+ patchCache[t].pitchEnv.sensitivity = (char)((float)patchCache[t].pitchEnv.sensitivity*1.27);
+ patchCache[t].pitchsustain = patchCache[t].pitchEnv.level[3];
+
+ // Calculate and cache TVA envelope stuff
+ patchCache[t].ampEnv = timSrc.partial[t].tva;
+ for (int i = 0; i < 4; i++)
+ patchCache[t].ampEnv.envlevel[i] = (char)((float)patchCache[t].ampEnv.envlevel[i]*1.27);
+ patchCache[t].ampEnv.level = (char)((float)patchCache[t].ampEnv.level*1.27);
+ float tvelo = ((float)timSrc.partial[t].tva.velosens / 100.0f);
+ float velo = fabs(tvelo-0.5f) * 2.0f;
+ velo *= 63.0f;
+ patchCache[t].ampEnv.velosens = (char)velo;
+ if (tvelo<0.5f)
+ patchCache[t].ampenvdir = 1;
+ else
+ patchCache[t].ampenvdir = 0;
+
+ patchCache[t].ampbias[0] = FixBiaslevel(patchCache[t].ampEnv.biaspoint1, &patchCache[t].ampdir[0]);
+ patchCache[t].ampblevel[0] = 12 - patchCache[t].ampEnv.biaslevel1;
+ patchCache[t].ampbias[1] = FixBiaslevel(patchCache[t].ampEnv.biaspoint2, &patchCache[t].ampdir[1]);
+ patchCache[t].ampblevel[1] = 12 - patchCache[t].ampEnv.biaslevel2;
+ patchCache[t].ampdepth = patchCache[t].ampEnv.envvkf * patchCache[t].ampEnv.envvkf;
+ patchCache[t].ampsustain = patchCache[t].ampEnv.envlevel[3];
+ patchCache[t].amplevel = patchCache[t].ampEnv.level;
+
+ // Calculate and cache filter stuff
+ patchCache[t].filtEnv = timSrc.partial[t].tvf;
+ patchCache[t].tvfdepth = patchCache[t].filtEnv.envdkf;
+ patchCache[t].filtkeyfollow = FixKeyfollow(patchCache[t].filtEnv.keyfollow, &patchCache[t].keydir);
+ patchCache[t].filtEnv.envdepth = (char)((float)patchCache[t].filtEnv.envdepth * 1.27);
+ patchCache[t].tvfbias = FixBiaslevel(patchCache[t].filtEnv.biaspoint, &patchCache[t].tvfdir);
+ patchCache[t].tvfblevel = patchCache[t].filtEnv.biaslevel;
+ patchCache[t].filtsustain = patchCache[t].filtEnv.envlevel[3];
+
+ // Calculate and cache LFO stuff
+ patchCache[t].lfodepth = timSrc.partial[t].lfo.depth;
+ patchCache[t].lfoperiod = lfotable[(int)timSrc.partial[t].lfo.rate];
+ patchCache[t].lforate = timSrc.partial[t].lfo.rate;
+ patchCache[t].modsense = timSrc.partial[t].lfo.modsense;
+ }
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ patchCache[t].partialCount = partialCount;
+ patchCache[t].sustain = (timSrc.common.nosustain == 0);
+ }
+ //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", patchCache[0].waveform, patchCache[1].waveform, patchCache[2].waveform, patchCache[3].waveform);
+
+ if (isRhythm)
+ memcpy(drumCache[absTimbreNum - 128], patchCache, sizeof(patchCache));
+ else
+ AllStop();
+#if DISPLAYINSTR == 1
+ synth->printDebug("%s: Recache, param %d (timbre: %s), %d partials", name, patchNum, currentInstr, partialCount);
+ for (int i = 0; i < 4; i++) {
+ synth->printDebug(" %d: play=%s, pcm=%d, wave=%d", i, patchCache[i].playPartial ? "YES" : "NO", timSrc.partial[i].wg.pcmwave, timSrc.partial[i].wg.waveform);
+ }
+#endif
+}
+
+char *Part::getName() {
+ return name;
+}
+
+void Part::SetVolume(int vol) {
+ volume = voltable[vol];
+}
+
+void Part::SetPan(int pan) {
+ // FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct?
+ // FIXME:KG: There is no way to get a centred balance here... And the middle two
+ // pan settings have a distance of 1024, double the usual.
+ // Perhaps we should multiply by 516? 520? 520.111..?
+ // KG: Changed to 516, to mostly eliminate that jump in the middle
+ if (pan < 64) {
+ volumesetting.leftvol = 32767;
+ volumesetting.rightvol = (Bit16s)(pan * 516);
+ } else {
+ volumesetting.rightvol = 32767;
+ volumesetting.leftvol = (Bit16s)((127 - pan) * 516);
+ }
+ //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
+}
+
+void Part::PlayNote(PartialManager *partialManager, int f, int vel) {
+ int drumNum = -1; // Initialised to please compiler
+ int drumTimbre = -1; // As above
+ int freqNum;
+
+ if (isRhythm) {
+ if (f < 24 || f > 87) {
+ synth->printDebug("%s: Attempted to play invalid note %d", name, f);
+ return;
+ }
+ drumNum = f - 24;
+ drumTimbre = rhythmTemp[drumNum].timbre;
+ if (drumTimbre >= 94) {
+ synth->printDebug("%s: Attempted to play unmapped note %d!", name, f);
+ return;
+ }
+ memcpy(patchCache, drumCache[drumTimbre], sizeof(patchCache));
+ memcpy(&currentInstr, synth->mt32ram.params.timbres[128 + drumTimbre].timbre.common.name, 10);
+ currentInstr[10] = 0;
+ freqNum = MIDDLEC;
+ } else {
+ if (f < 12 || f > 108) {
+ synth->printDebug("%s (%s): Attempted to play invalid note %d", name, currentInstr, f);
+ return;
+ }
+ freqNum = f;
+ }
+ // 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?
+ if (!isRhythm) {
+ for (int i = 0; i < MAXPOLY; i++) {
+ if (polyTable[i].isActive() && (polyTable[i].note == f)) {
+ //AbortPoly(&polyTable[i]);
+ StopNote(f);
+ break;
+ }
+ }
+ }
+
+ unsigned int needPartials = patchCache[0].partialCount;
+
+ if (needPartials > partialManager->GetFreePartialCount()) {
+ if (!partialManager->FreePartials(needPartials, partNum)) {
+ synth->printDebug("%s (%s): Insufficient free partials to play note %d (vel=%d)", name, currentInstr, f, vel);
+ return;
+ }
+ }
+ // Find free note
+ int m;
+ for (m = 0; m < MAXPOLY; m++) {
+ if (!polyTable[m].isActive()) {
+ break;
+ }
+ }
+ if (m == MAXPOLY) {
+ synth->printDebug("%s (%s): No free poly to play note %d (vel %d)", name, currentInstr, f, vel);
+ return;
+ }
+
+ dpoly *tpoly = &polyTable[m];
+ Bit16s freq = freqtable[freqNum];
+
+ tpoly->isPlaying = true;
+ tpoly->note = f;
+ tpoly->isDecay = false;
+ tpoly->freq = freq;
+ tpoly->freqnum = freqNum;
+ tpoly->vel = vel;
+ tpoly->pedalhold = false;
+
+ bool allnull = true;
+ for (int x = 0; x < 4; x++) {
+ if (patchCache[x].playPartial) {
+ tpoly->partials[x] = 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);
+
+ if (isRhythm) {
+ tpoly->bendptr = &drumBend;
+ tpoly->pansetptr = &drumPan[drumNum];
+ tpoly->reverb = rhythmTemp[drumNum].reverbSwitch > 0;
+ } else {
+ tpoly->bendptr = &bend;
+ tpoly->pansetptr = &volumesetting;
+ tpoly->reverb = patchTemp->patch.reverbSwitch > 0;
+ }
+ tpoly->sustain = patchCache[0].sustain;
+ tpoly->volumeptr = &volume;
+
+ for (int x = 0; x < 4; x++) {
+ if (tpoly->partials[x] != NULL) {
+ tpoly->partials[x]->startPartial(tpoly, &patchCache[x], tpoly->partials[patchCache[x].structurePair]);
+ }
+ }
+
+#if DISPLAYINSTR == 1
+ if (isRhythm) {
+ synth->printDebug("%s (%s): starting note poly %d (drum %d, timbre %d) - Vel %d Vol %d", name, currentInstr, m, drumNum, drumTimbre, vel, volume);
+ } else {
+ synth->printDebug("%s (%s): starting note poly %d - Vel %d Freq %d Vol %d", name, currentInstr, m, vel, f, volume);
+ }
+#endif
+}
+
+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 < MAXPOLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+ if (tpoly->isPlaying) {
+ StartDecayPoly(tpoly);
+ }
+ }
+}
+
+void Part::StopPedalHold() {
+ for (int q = 0; q < MAXPOLY; q++) {
+ dpoly *tpoly;
+ tpoly = &polyTable[q];
+ if (tpoly->isActive() && tpoly->pedalhold)
+ StopNote(tpoly->note);
+ }
+}
+
+void Part::StopNote(int f) {
+ // Non-sustaining instruments ignore stop note commands.
+ // They die away eventually anyway
+ //if (!tpoly->sustain) return;
+
+#if DISPLAYINSTR == 1
+ synth->printDebug("%s (%s): stopping note %d", name, currentInstr, f);
+#endif
+
+ if (f != -1) {
+ for (int q = 0; q < MAXPOLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+ if (tpoly->isPlaying && tpoly->note == f) {
+ if (holdpedal)
+ tpoly->pedalhold = true;
+ else if (tpoly->sustain)
+ StartDecayPoly(tpoly);
+ }
+ }
+ return;
+ }
+
+ // Find oldest note... yes, the MT-32 can be reconfigured to kill different note first
+ // This is simplest
+ int oldest = -1;
+ Bit64s oldage = -1;
+
+ for (int q = 0; q < MAXPOLY; 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/backends/midi/mt32/part.h b/backends/midi/mt32/part.h
new file mode 100644
index 0000000000..302ca4d2fb
--- /dev/null
+++ b/backends/midi/mt32/part.h
@@ -0,0 +1,90 @@
+/* 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
+
+#define AMPENV 0
+#define FILTENV 1
+#define PITCHENV 2
+
+#define MAXPOLY 64
+
+namespace MT32Emu {
+
+class PartialManager;
+class Synth;
+
+class Part {
+private:
+ Synth *synth; // Only used for sending debug output
+
+ // Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)
+ MemParams::PatchTemp *patchTemp;
+ TimbreParam *timbreTemp;
+ //... and for rhythm
+ MemParams::RhythmTemp *rhythmTemp;
+
+ bool isRhythm;
+ bool init;
+ int partNum;
+
+ char name[8]; // "Part 1".."Part 8", "Rhythm"
+ char currentInstr[11];
+
+ bool holdpedal;
+
+ volset volumesetting;
+
+ PatchCache patchCache[4];
+
+ Bit32u bend;
+ Bit32s volume;
+
+ dpoly polyTable[MAXPOLY];
+
+ void AbortPoly(dpoly *poly);
+
+public:
+ Part(Synth *synth, int usePartNum);
+ char *getName();
+ void PlayNote(PartialManager *partialManager, int f, int vel);
+ void StopNote(int f);
+ void AllStop();
+ void SetVolume(int vol);
+ void SetPan(int vol);
+ void SetBend(int vol);
+ void SetModulation(int vol);
+ void SetPatch(int patchnum);
+ void SetHoldPedal(bool pedalval);
+ void StopPedalHold();
+ void RefreshPatch();
+ void RefreshDrumCache();
+ void setPatch(PatchParam *patch);
+ void setTimbre(TimbreParam *timbre);
+ unsigned int getAbsTimbreNum();
+
+ int FixKeyfollow(int srckey, int *dir);
+ int FixBiaslevel(int srcpnt, int *dir);
+};
+
+}
+#endif
diff --git a/backends/midi/mt32/partial.cpp b/backends/midi/mt32/partial.cpp
index a5dddfed1f..5a849ae4ad 100644
--- a/backends/midi/mt32/partial.cpp
+++ b/backends/midi/mt32/partial.cpp
@@ -1,601 +1,884 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
*
- * 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.
+ * 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:
*
- * $Header$
+ * 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.
*/
-// Implementation of the MT-32 partial class
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
-#include "backends/midi/mt32/synth.h"
-#include "backends/midi/mt32/partial.h"
+#include "mt32emu.h"
-INLINE void CPartialMT32::generateSamples(int16 * partialBuf, long length) {
- if (!isActive) return;
- if (alreadyOutputed) return;
-
+using namespace MT32Emu;
- alreadyOutputed = true;
+Partial::Partial(Synth *useSynth) {
+ this->synth = useSynth;
+ ownerPart = -1;
+ poly = NULL;
+ pair = NULL;
+};
- // Generate samples
+int Partial::getOwnerPart() {
+ return ownerPart;
+}
- int r;
- int i;
- int32 envval, ampval, filtval;
- soundaddr *pOff = &partCache->partialOff;
- int noteval = partCache->keyedval;
- for(i=0;i<length;i++) {
- int32 ptemp = 0;
+bool Partial::isActive() {
+ return ownerPart > -1;
+}
- if(partCache->envs[AMPENV].sustaining) {
- ampval = partCache->ampEnvCache;
- } else {
- if(partCache->envs[AMPENV].count<=0) {
-
- ampval = getAmpEnvelope(partCache,tmppoly);
- isActive = partCache->playPartial;
-//TODO: check what is going on here
-// if (ampval < 0) ampval = 0;
- //printf("%d %d\n", (int)ampval, (int)isActive);
- if(!isActive) {
- tmppoly->partActive[timbreNum] = false;
- tmppoly->isActive = tmppoly->partActive[0] || tmppoly->partActive[1] || tmppoly->partActive[2] || tmppoly->partActive[3];
- }
- if(ampval>=128) ampval = 127;
-
- ampval = amptable[ampval];
- int tmpvel = tmppoly->vel;
- if(tcache->ampenvdir==1) tmpvel = 127-tmpvel;
- ampval = (ampval * ampveltable[tmpvel][(int)tcache->ampEnv.velosens]) >> 8;
- //if(partCache->envs[AMPENV].sustaining)
- partCache->ampEnvCache = ampval;
- } else {
- ampval = partCache->ampEnvCache;
+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;
}
- --partCache->envs[AMPENV].count;
}
-
+ if (pair != NULL) {
+ pair->pair = NULL;
+ }
+ }
+}
+
+void Partial::initKeyFollow(int freqNum) {
+ // Setup partial keyfollow
+ // Note follow relative to middle C
+ int keyfollow;
+ int realfol = (freqNum * 2 - MIDDLEC * 2) / 2;
+ int antirealfol = (MIDDLEC * 2 - freqNum * 2) / 2;
+ // Calculate keyfollow for pitch
+ switch(patchCache->pitchkeydir) {
+ case -1:
+ keyfollow = (antirealfol * patchCache->pitchkeyfollow) >> 12;
+ break;
+ case 0:
+ keyfollow = 0;
+ break;
+ case 1:
+ keyfollow = (realfol * patchCache->pitchkeyfollow) >> 12;
+ break;
+ default:
+ keyfollow = 0; // Please the compiler
+ }
+ if ((patchCache->pitchkeyfollow>4096) && (patchCache->pitchkeyfollow<4200)) {
+ // Be sure to round up on keys below MIDDLEC
+ if (realfol < 0)
+ keyfollow++;
+ }
+ noteVal = (keyfollow + patchCache->pitchshift);
+ if (noteVal > 108)
+ noteVal = 108;
+ if (noteVal < 12)
+ noteVal = 12;
+
+ // Calculate keyfollow for filter
+ switch(patchCache->keydir) {
+ case -1:
+ keyfollow = (antirealfol * patchCache->filtkeyfollow) >> 12;
+ break;
+ case 0:
+ keyfollow = freqNum;
+ break;
+ case 1:
+ keyfollow = (realfol * patchCache->filtkeyfollow) >> 12;
+ break;
+ }
+ if (keyfollow > 108)
+ keyfollow = 108;
+ if (keyfollow < -108)
+ keyfollow = -108;
+ filtVal = keytable[keyfollow + 108];
+ realVal = keytable[realfol + 108];
+}
+
+void Partial::startPartial(dpoly *usePoly, 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->mix;
+ structurePosition = patchCache->structurePosition;
+
+ play = true;
+ initKeyFollow(poly->freqnum);
+ 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;
+ }
+ ampEnvCache = 0;
+ pitchEnvCache = 0;
+ pitchSustain = false;
+ loopPos = 0;
+ partialOff.pcmabs = 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, ampval;
+ Bit32s ptemp = 0;
+ if (envs[AMPENV].sustaining)
+ ampval = ampEnvCache;
+ else {
+ if (envs[AMPENV].count<=0) {
+ ampval = getAmpEnvelope();
+ if (!play) {
+ deactivate();
+ break;
+ }
+ if (ampval < 0) {
+ //TODO: check what is going on here
+ synth->printDebug("ampval<0! ampval=%ld, active=%d", ampval, isActive());
+ ampval = 0;
+ } else if (ampval > 127) {
+ ampval = 127;
+ }
+
+ ampval = voltable[ampval];
+ int tmpvel = poly->vel;
+ if (patchCache->ampenvdir == 1)
+ tmpvel = 127 - tmpvel;
+ ampval = (ampval * ampveltable[tmpvel][(int)patchCache->ampEnv.velosens]) >> 8;
+ //if (envs[AMPENV].sustaining)
+ ampEnvCache = ampval;
+ } else
+ ampval = ampEnvCache;
+ --envs[AMPENV].count;
+ }
+
int delta = 0x10707;
// Calculate Pitch envelope
int lfoat = 0x1000;
int pdep;
- if(partCache->pitchsustain) {
+ if (pitchSustain) {
// Calculate LFO position
// LFO does not kick in completely until pitch envelope sustains
-
- if(tcache->lfodepth>0) {
- partCache->lfopos++;
-
- if(partCache->lfopos>=tcache->lfoperiod) partCache->lfopos = 0;
- int lfoatm = (partCache->lfopos << 16) / tcache->lfoperiod;
-
+ if (patchCache->lfodepth>0) {
+ lfoPos++;
+ if (lfoPos >= patchCache->lfoperiod)
+ lfoPos = 0;
+ int lfoatm = (lfoPos << 16) / patchCache->lfoperiod;
int lfoatr = sintable[lfoatm];
-
- lfoat = lfoptable[tcache->lfodepth][lfoatr];
+ lfoat = lfoptable[patchCache->lfodepth][lfoatr];
}
- pdep = partCache->pitchEnvCache;
-
-
+ pdep = pitchEnvCache;
} else {
- envval = getPitchEnvelope(partCache,tmppoly);
- int pd=tcache->pitchEnv.depth;
+ envval = getPitchEnvelope();
+ int pd = patchCache->pitchEnv.depth;
pdep = penvtable[pd][envval];
- if(partCache->pitchsustain) partCache->pitchEnvCache = pdep;
-
+ if (pitchSustain)
+ pitchEnvCache = pdep;
}
-
// Get waveform - either PCM or synthesized sawtooth or square
-
-
- if (tcache->PCMPartial) {
+ if (patchCache->PCMPartial) {
// PCM partial
-
- if(!partCache->PCMDone) {
-
- int addr,len,tmppcm;
- partialTable *tPCM = &tcache->convPCM;
-
- if(tPCM->aggSound==-1) {
- delta = wavtabler[tPCM->pcmnum][noteval];
- addr = tPCM->addr;
- len = tPCM->len;
-
- } else {
- tmppcm = LoopPatterns[tPCM->aggSound][partCache->looppos];
- addr = PCM[tmppcm].addr;
- len = PCM[tmppcm].len;
- delta = looptabler[tPCM->aggSound][partCache->looppos][noteval];
- }
-
- if(ampval>0) {
- int ra,rb,dist;
- int taddr;
- if(delta<0x10000) {
- // Linear sound interpolation
-
- taddr = addr + pOff->pcmoffs.pcmplace;
- ra = romfile[taddr];
- rb = romfile[taddr+1];
-
- dist = rb-ra;
- r = (ra + ((dist * (int32)(pOff->pcmoffs.pcmoffset>>8)) >>8));
-
+ int addr,len;
+ sampleTable *tPCM = &synth->PCMList[patchCache->pcm];
+
+ if (tPCM->aggSound == -1) {
+ delta = wavtabler[tPCM->pcmnum][noteVal];
+ addr = tPCM->addr;
+ len = tPCM->len;
+ if (partialOff.pcmoffs.pcmplace >= len) {
+ if (tPCM->loop) {
+ partialOff.pcmabs = 0;
} else {
-
- //r = romfile[addr + pOff->pcmoffs.pcmplace];
- // 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 appoximates this as fast as possible
- int idelta = delta >> 16;
- int ix;
- taddr = addr + pOff->pcmoffs.pcmplace;
- ra = 0;
- for(ix=0;ix<idelta;ix++) {
- ra += romfile[taddr++];
- }
- r = ra / idelta;
-
-
-
+ play = false;
+ deactivate();
+ break;
}
- } else r = 0;
-
- ptemp = r;
-
- if ((pOff->pcmoffs.pcmplace) >= len) {
- if(tPCM->aggSound==-1) {
- if(tPCM->loop) {
- pOff->pcmabs = 0;
- } else {
- partCache->PCMDone = true;
- partCache->playPartial = false;
- }
+ }
+ } else {
+ int tmppcm = LoopPatterns[tPCM->aggSound][loopPos];
+ delta = looptabler[tPCM->aggSound][loopPos][noteVal];
+ addr = synth->PCM[tmppcm].addr;
+ len = synth->PCM[tmppcm].len;
+ if (partialOff.pcmoffs.pcmplace >= len) {
+ loopPos++;
+ if (LoopPatterns[tPCM->aggSound][loopPos]==-1)
+ loopPos=0;
+ partialOff.pcmabs = 0;
+ }
+ }
- } else {
- partCache->looppos++;
- if(LoopPatterns[tPCM->aggSound][partCache->looppos]==-1) partCache->looppos=0;
- pOff->pcmabs = 0;
+ if (ampval>0) {
+ int ra,rb,dist;
+ int taddr;
+ if (delta<0x10000) {
+ // Linear sound interpolation
+ taddr = addr + partialOff.pcmoffs.pcmplace;
+ if (taddr >= ROMSIZE) {
+ synth->printDebug("Overflow ROMSIZE!");
+ taddr = ROMSIZE - 1;
}
- //LOG_MSG("tPCM %d loops %d done %d playPart %d", tPCM->pcmnum, tPCM->loop, partCache->PCMDone, partCache->playPartial);
-
+ ra = synth->romfile[taddr];
+ rb = synth->romfile[taddr+1];
+ dist = rb-ra;
+ ptemp = (ra + ((dist * (Bit32s)(partialOff.pcmoffs.pcmoffset>>8)) >>8));
+ } else {
+ //r = romfile[addr + partialOff.pcmoffs.pcmplace];
+ // 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 = addr + partialOff.pcmoffs.pcmplace;
+ ra = 0;
+ for (int ix=0;ix<idelta;ix++)
+ ra += synth->romfile[taddr++];
+ ptemp = ra / idelta;
}
-
-
}
-
} else {
// Synthesis partial
- int divis, hdivis, ofs, ofs3, toff;
- int minorplace;
+ int divis = divtable[noteVal] >> 15;
- int wf = tcache->waveform ;
+ partialOff.pcmoffs.pcmplace %= (Bit16u)divis;
- divis = divtable[noteval]>>15;
+ if (ampval > 0) {
+ int wf = patchCache->waveform ;
+ int toff = partialOff.pcmoffs.pcmplace;
+ int minorplace = partialOff.pcmoffs.pcmoffset >> 14;
- if(pOff->pcmoffs.pcmplace>=divis) pOff->pcmoffs.pcmplace = (uint16)(pOff->pcmoffs.pcmplace-divis);
-
- toff = pOff->pcmoffs.pcmplace;
- minorplace = pOff->pcmoffs.pcmoffset >> 14;
-
- int pa, pb;
+ int pa, pb;
- if(ampval>0) {
+ Bit32s filtval = getFiltEnvelope();
- filtval = getFiltEnvelope((int16)ptemp,partCache,tmppoly);
+ //synth->printDebug("Filtval: %d", filtval);
- //LOG_MSG("Filtval: %d", filtval);
-
- if(wf==0) {
+ if (wf==0) {
// Square waveform. Made by combining two pregenerated bandlimited
// sawtooth waveforms
// Pulse width is not yet correct
-
- hdivis = divis >> 1;
- int divmark = smalldivtable[noteval];
- //int pw = (tcache->pulsewidth * pulsemod[filtval]) >> 8;
-
-
- ofs = toff % (hdivis);
- ofs3 = toff + ((divmark*pulsetable[partCache->pulsewidth])>>16);
+ int hdivis = divis >> 1;
+ int divmark = smalldivtable[noteVal];
+
+ if (hdivis == 0) {
+ synth->printDebug("ERROR: hdivis=0 generating square wave, this should never happen!");
+ hdivis = 1;
+ }
+ int ofs = toff % hdivis;
+
+ int ofs3 = toff + ((divmark * pulsetable[pulsewidth]) >> 16);
ofs3 = ofs3 % (hdivis);
-
- pa = waveforms[1][noteval][(ofs<<2)+minorplace];
- pb = waveforms[0][noteval][(ofs3<<2)+minorplace];
- //ptemp = pa+pb+pulseoffset[tcache->pulsewidth];
- ptemp = (pa+pb)*4;
-
+
+ pa = waveforms[1][noteVal][(ofs << 2)+minorplace];
+ pb = waveforms[0][noteVal][(ofs3 << 2)+minorplace];
+ ptemp = (pa + pb) * 4;
+
// Non-bandlimited squarewave
/*
- ofs = (divis*pulsetable[tcache->pulsewidth])>>8;
- if(toff < ofs) {
+ ofs = (divis*pulsetable[patchCache->pulsewidth])>>8;
+ if (toff < ofs)
ptemp = 1 * WGAMP;
- } else {
+ else
ptemp = -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
- // TODO: This area here crashes DosBox due to read overflow
- if(toff < sawtable[noteval][partCache->pulsewidth]) {
- ptemp = waveforms[2][noteval][(toff<<2)+minorplace];
- } else {
- ptemp = waveforms[3][noteval][(toff<<2)+minorplace];
- }
- ptemp = ptemp *4;
-
-// This is dosbox 0.62 canadacow's code. Reported to be worse than above 0.61 code
-#if 0
- uint offsetpos = (toff<<2)+minorplace;
- //int a = 0;
- if(toff < sawtable[noteval][partCache->pulsewidth]) {
- while(offsetpos>waveformsize[2][noteval]) {
- offsetpos-=waveformsize[2][noteval];
- }
- ptemp = waveforms[2][noteval][offsetpos];
- } else {
- while(offsetpos>waveformsize[3][noteval]) {
- offsetpos-=waveformsize[3][noteval];
- }
- ptemp = waveforms[3][noteval][offsetpos];
- }
- ptemp = ptemp *4;
-#endif
-
- // ptemp = (int)(sin((double)toff / 100.0) * 100.0);
- //ptemp = pa;
+ int waveoff = (toff << 2) + minorplace;
+ if (toff < sawtable[noteVal][pulsewidth])
+ ptemp = waveforms[2][noteVal][waveoff % waveformsize[2][noteVal]];
+ else
+ ptemp = waveforms[3][noteVal][waveoff % waveformsize[3][noteVal]];
+ ptemp = ptemp * 4;
// This is the correct way
// Seems slow to me (though bandlimited) -- doesn't seem to
// sound any better though
/*
hdivis = divis >> 1;
- int divmark = smalldivtable[noteval];
- //int pw = (tcache->pulsewidth * pulsemod[filtval]) >> 8;
-
-
+ int divmark = smalldivtable[noteVal];
+ //int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8;
+
ofs = toff % (hdivis);
- ofs3 = toff + ((divmark*pulsetable[tcache->pulsewidth])>>16);
+ ofs3 = toff + ((divmark*pulsetable[patchCache->pulsewidth])>>16);
ofs3 = ofs3 % (hdivis);
-
- pa = waveforms[0][noteval][ofs];
- pb = waveforms[1][noteval][ofs3];
- ptemp = ((pa+pb) * waveforms[3][noteval][toff]) / WGAMP;
+
+ pa = waveforms[0][noteVal][ofs];
+ pb = waveforms[1][noteVal][ofs3];
+ ptemp = ((pa+pb) * waveforms[3][noteVal][toff]) / WGAMP;
ptemp = ptemp *4;
*/
-
-
-
}
-
- //Very exact filter
- //ptemp[t] = (int)iir_filter((float)ptemp[t],&partCache->history[t],filtcoeff[filtval][tcache->filtEnv.resonance]);
- if(filtval>((FILTERGRAN*15)/16)) filtval = ((FILTERGRAN*15)/16);
- ptemp = (int32)(usefilter)((float)ptemp,&partCache->history[0],filtcoeff[filtval][(int)tcache->filtEnv.resonance], tcache->filtEnv.resonance);
- } else ptemp = 0;
-
- //ptemp[t] = Moog1(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance);
- //ptemp[t] = Moog2(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance);
- //ptemp[t] = simpleLowpass(ptemp[t],&partCache->history[t],(float)filtval/8192.0,tcache->filtEnv.resonance);
-
- // Use this to mute analogue synthesis
- // ptemp = 0;
-
+ //Very exact filter
+ if (filtval > ((FILTERGRAN * 15) / 16))
+ filtval = ((FILTERGRAN * 15) / 16);
+ ptemp = (Bit32s)floor((usefilter)((float)ptemp, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance));
+ }
}
-
- // Build delta for position of next sample
- /*
- delta = (delta * tcache->fineshift)>>12;
- delta = (delta * pdep)>>12;
- delta = (delta * lfoat)>>12;
- if(tcache->useBender) delta = (delta * *tmppoly->bendptr)>>12;
- */
+ // Build delta for position of next sample
// Fix delta code
- int64 tdelta = (int64)delta;
- tdelta = (tdelta * tcache->fineshift)>>12;
+ Bit64s tdelta = (Bit64s)delta;
+ tdelta = (tdelta * patchCache->fineshift)>>12;
tdelta = (tdelta * pdep)>>12;
tdelta = (tdelta * lfoat)>>12;
- if(tcache->useBender) tdelta = (tdelta * *tmppoly->bendptr)>>12;
+ if (patchCache->useBender)
+ tdelta = (tdelta * *poly->bendptr)>>12;
// Add calculated delta to our waveform offset
- pOff->pcmabs+=(int)tdelta;
+ partialOff.pcmabs += (int)tdelta;
// Put volume envelope over generated sample
ptemp = (ptemp * ampval) >> 9;
- ptemp = (ptemp * *tmppoly->volumeptr) >> 7;
-
- partCache->envs[AMPENV].envpos++;
- partCache->envs[PITCHENV].envpos++;
- partCache->envs[FILTENV].envpos++;
-
- *partialBuf++ = (int16)ptemp;
- }
+ ptemp = (ptemp * *poly->volumeptr) >> 7;
+ envs[AMPENV].envpos++;
+ envs[PITCHENV].envpos++;
+ envs[FILTENV].envpos++;
+ *partialBuf++ = (Bit16s)ptemp;
+ }
+ if (++length > 0)
+ memset(partialBuf, 0, length * 2);
+ return &myBuffer[0];
}
-INLINE void CPartialMT32::mixBuffers(int16 * buf1, int16 *buf2, int len) {
- // Early exit if no need to mix
- if(tibrePair==NULL) return;
-
-#if USE_MMX == 0
- int i;
- for(i=0;i<len;i++) {
- int32 tmp1 = buf1[i];
- int32 tmp2 = buf2[i];
- tmp1 += tmp2;
- buf1[i] = (int16)tmp1;
- }
-#else
- len = (len>>2)+4;
-#ifdef I_ASM
- __asm {
- mov ecx, len
- 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, len);
-#endif
+Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
+ if (buf1 == NULL)
+ return buf2;
+ if (buf2 == NULL)
+ return buf1;
+
+ Bit16s *outBuf = buf1;
+#if 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;
}
-INLINE void CPartialMT32::mixBuffersRingMix(int16 * buf1, int16 *buf2, int len) {
-#if USE_MMX != 2
- int i;
- for(i=0;i<len;i++) {
+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 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[i]) / 8192.0;
- b = ((float)buf2[i]) / 8192.0;
+ 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[i] = (int16)(a * 8192.0);
-
- //buf1[i] = (int16)(((int32)buf1[i] * (int32)buf2[i]) >> 10) + buf1[i];
- }
-#else
- len = (len>>2)+4;
-#ifdef I_ASM
- __asm {
- mov ecx, len
- 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
+ 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];
}
-#else
- atti386_mixBuffersRingMix(buf1, buf2, len);
-#endif
-#endif
+ return outBuf;
}
-INLINE void CPartialMT32::mixBuffersRing(int16 * buf1, int16 *buf2, int len) {
-#if USE_MMX != 2
- int i;
- for(i=0;i<len;i++) {
- float a, b;
- a = ((float)buf1[i]) / 8192.0;
- b = ((float)buf2[i]) / 8192.0;
- a *= b;
- if(a>1.0) a = 1.0;
- if(a<-1.0) a = -1.0;
- buf1[i] = (int16)(a * 8192.0);
- //buf1[i] = (int16)(((int32)buf1[i] * (int32)buf2[i]) >> 10);
+Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
+ if (buf1 == NULL) {
+ return NULL;
}
-#else
- len = (len>>2)+4;
-#ifdef I_ASM
- __asm {
- mov ecx, len
- 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
+ if (buf2 == NULL) {
+ return NULL;
}
-#else
- atti386_mixBuffersRing(buf1, buf2, len);
-#endif
-#endif
-}
-INLINE void CPartialMT32::mixBuffersStereo(int16 *buf1, int16 *buf2, int16 *outBuf, int len) {
- int i,m;
- m=0;
- for(i=0;i<len;i++) {
- *outBuf++ = (*buf1);
+ Bit16s *outBuf = buf1;
+#if 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++;
- *outBuf++ = (*buf2);
buf2++;
}
-
+ return outBuf;
}
-bool CPartialMT32::produceOutput(int16 * partialBuf, long length) {
- if (!isActive) return false;
- if (alreadyOutputed) return false;
- int i;
+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++;
+ }
+ }
+}
- //alreadyOutputed = true;
+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;
+ }
- memset(&pairBuffer[0],0,length*4);
- memset(&myBuffer[0],0,length*4);
+ Bit16s *pairBuf = NULL;
// Check for dependant partial
- if(tibrePair != NULL) {
- if ((tibrePair->ownerChan == this->ownerChan) && (!tibrePair->alreadyOutputed)) {
- tibrePair->generateSamples(pairBuffer,length);
- }
- } else {
- if((useMix!=0) && (useMix != 3)){
- // Generate noise for parialless ring mix
- for(i=0;i<length;i++) pairBuffer[i] = smallnoise[i] << 2;
+ 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;
}
- generateSamples(myBuffer, length);
-/* FILE *fo = fopen("/tmp/samp.raw", "a");
- for(i = 0; i < length; i++)
- fwrite(myBuffer + i, 1, 2, fo);
- fclose(fo);
-*/
- int16 * p1buf, * p2buf;
-
- if((partNum==0) || ((partNum==1) && (tibrePair==NULL))) {
- p1buf = &myBuffer[0];
- p2buf = &pairBuffer[0];
+ 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 = &myBuffer[0];
- p1buf = &pairBuffer[0];
+ p2buf = myBuf;
+ p1buf = pairBuf;
}
-
-// LOG_MSG("useMix: %d", useMix);
-
- switch(useMix) {
+
+ //synth->printDebug("mixType: %d", mixType);
+
+ Bit16s *mixedBuf;
+ switch(mixType) {
case 0:
// Standard sound mix
- mixBuffers(p1buf, p2buf, length);
+ mixedBuf = mixBuffers(p1buf, p2buf, length);
break;
+
case 1:
// Ring modulation with sound mix
- mixBuffersRingMix(p1buf, p2buf, length);
+ mixedBuf = mixBuffersRingMix(p1buf, p2buf, length);
break;
+
case 2:
// Ring modulation alone
- mixBuffersRing(p1buf, p2buf, length);
+ mixedBuf = mixBuffersRing(p1buf, p2buf, length);
break;
+
case 3:
- // Stereo mixing. One partial to one channel, one to another.
+ // 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;
- break;
+
default:
- mixBuffers(p1buf, p2buf, length);
+ mixedBuf = mixBuffers(p1buf, p2buf, length);
break;
}
-
-
- int m;
- m = 0;
- int16 leftvol, rightvol;
- if (!tmppoly->isRy) {
- leftvol = tmppoly->pansetptr->leftvol;
- rightvol = tmppoly->pansetptr->rightvol;
+
+ if (mixedBuf == NULL)
+ return false;
+
+ Bit16s leftvol, rightvol;
+ leftvol = poly->pansetptr->leftvol;
+ rightvol = poly->pansetptr->rightvol;
+
+#if 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;
+
+ envstatus *tStat = &envs[FILTENV];
+
+ keyfollow = filtVal;
+ realfollow = realVal;
+
+ int fr = poly->freqnum;
+
+ if (tStat->decaying) {
+ reshigh = tStat->envbase;
+ reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ if (tStat->envpos >= tStat->envsize)
+ reshigh = 0;
} else {
- leftvol = (int16)drumPan[tmppoly->pcmnum][0];
- rightvol = (int16)drumPan[tmppoly->pcmnum][1];
+ if (tStat->envstat==4) {
+ reshigh = patchCache->filtsustain;
+ if (!poly->sustain) {
+ startDecay(FILTENV, 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]] * timekeytable[(int)patchCache->filtEnv.envtkf][poly->freqnum]) >> 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;
}
-#if USE_MMX == 0
- for(i=0;i<length;i++) {
- partialBuf[m] = (int16)(((int32)p1buf[i] * (int32)leftvol) >> 16);
- m++;
- partialBuf[m] = (int16)(((int32)p1buf[i] * (int32)rightvol) >> 16);
- m++;
+ 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) {
+ //synth->printDebug("Cutoff before %d", cutoff);
+ if (patchCache->tvfdir == 0) {
+ if (fr < bias) {
+ dist = bias - fr;
+ cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
+ }
+ } else {
+ // > Bias
+ if (fr > bias) {
+ dist = fr - bias;
+ cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
+ }
+
+ }
+ //synth->printDebug("Cutoff after %d", cutoff);
+ }
+
+ depth = (depth * fildeptable[patchCache->tvfdepth][fr]) >> 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 = nfilttable[fr][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 poly->reverb;
+}
+
+Bit32s Partial::getAmpEnvelope() {
+ Bit32s tc;
+
+ envstatus *tStat = &envs[AMPENV];
+
+ 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(AMPENV, 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]] * timekeytable[(int)patchCache->ampEnv.envtkf][poly->freqnum]) >> 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;
}
-#else
- long quadlen = (length >> 1)+2;
-#ifdef I_ASM
- __asm {
- mov ecx,quadlen
- 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, p1buf
-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
+
+ // Prevlevel storage is bottle neck
+ tStat->prevlevel = tc;
+
+ //Bias level crap stuff now
+
+ int dist, bias;
+
+ for (int i = 0; i < 2; i++) {
+ if (patchCache->ampblevel[i]!=0) {
+ bias = patchCache->ampbias[i];
+ if (patchCache->ampdir[i]==0) {
+ // < Bias
+ if (poly->freqnum < bias) {
+ dist = bias-poly->freqnum;
+ tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
+ }
+ } else {
+ // > Bias
+ if (poly->freqnum > bias) {
+ dist = poly->freqnum-bias;
+ tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
+ }
+ }
+ }
}
-#else
- atti386_PartProductOutput(quadlen, leftvol, rightvol, partialBuf, p1buf);
-#endif
-#endif
-
- return true;
+ return tc;
+}
+
+Bit32s Partial::getPitchEnvelope() {
+ envstatus *tStat = &envs[PITCHENV];
+
+ 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(PITCHENV, 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]] * timekeytable[(int)patchCache->pitchEnv.timekeyfollow][poly->freqnum]) >> 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(AMPENV, envs[AMPENV].prevlevel);
+ startDecay(FILTENV, envs[FILTENV].prevlevel);
+ startDecay(PITCHENV, envs[PITCHENV].prevlevel);
+ pitchSustain = false;
}
+
+void Partial::startDecay(int envnum, Bit32s startval) {
+ envstatus *tStat = &envs[envnum];
+
+ tStat->sustaining = false;
+ tStat->decaying = true;
+ tStat->envpos = 0;
+ tStat->envbase = startval;
+
+ switch(envnum) {
+ case AMPENV:
+ tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * timekeytable[(int)patchCache->ampEnv.envtkf][poly->freqnum]) >> 8;
+ tStat->envdist = -startval;
+ break;
+ case FILTENV:
+ tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * timekeytable[(int)patchCache->filtEnv.envtkf][poly->freqnum]) >> 8;
+ tStat->envdist = -startval;
+ break;
+ case PITCHENV:
+ tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * timekeytable[(int)patchCache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8 ;
+ tStat->envdist = patchCache->pitchEnv.level[4] - startval;
+ break;
+ default:
+ break;
+ }
+ tStat->envsize++;
+};
diff --git a/backends/midi/mt32/partial.h b/backends/midi/mt32/partial.h
index e3af4ad5a9..20160dca5f 100644
--- a/backends/midi/mt32/partial.h
+++ b/backends/midi/mt32/partial.h
@@ -1,115 +1,124 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
*
- * 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.
+ * 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:
*
- * $Header$
+ * 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 CPARTIALMT32_H
-#define CPARTIALMT32_H
+#ifndef MT32EMU_PARTIAL_H
+#define MT32EMU_PARTIAL_H
-#include "backends/midi/mt32/structures.h"
+namespace MT32Emu {
+
+struct envstatus {
+ Bit32s envpos;
+ Bit32s envstat;
+ Bit32s envbase;
+ Bit32s envdist;
+ Bit32s envsize;
+
+ bool sustaining;
+ bool decaying;
+ Bit32s prevlevel;
+
+ Bit32s counter;
+ Bit32s count;
+};
+
+class Synth;
// Class definition of MT-32 partials. 32 in all.
-class CPartialMT32 {
+class Partial {
private:
- int useMix;
- int partNum;
+ Synth *synth; // Only used for sending debug output
- int pN;
+ 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
+ int noteVal;
- int16 myBuffer[2048];
- // For temporary output of paired buffer
- int16 pairBuffer[2048];
+ // Keyfollowed filter values
+ int realVal;
+ int filtVal;
- void mixBuffers(int16 * buf1, int16 * buf2, int len);
- void mixBuffersRingMix(int16 * buf1, int16 * buf2, int len);
- void mixBuffersRing(int16 * buf1, int16 * buf2, int len);
- void mixBuffersStereo(int16 * buf1, int16 * buf2, int16 * outBuf, int len);
+ envstatus envs[3];
+ int pulsewidth;
-public:
- patchCache *tcache;
- patchCache cachebackup[4];
+ Bit32u lfoPos;
+ soundaddr partialOff;
+
+ Bit32u ampEnvCache;
+ Bit32u pitchEnvCache;
+
+ float history[32];
+
+ bool pitchSustain;
+
+ int loopPos;
- //FILE *fp;
- //FILE *fp2;
+ dpoly *poly;
- dpoly::partialStatus *partCache;
+ 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);
- CPartialMT32 *tibrePair;
- bool isActive;
+ Bit32s getFiltEnvelope();
+ Bit32s getAmpEnvelope();
+ Bit32s getPitchEnvelope();
+
+ void initKeyFollow(int freqNum);
+
+public:
+ PatchCache *patchCache;
+ PatchCache cachebackup;
+
+ Partial *pair;
bool alreadyOutputed;
- int ownerChan;
- int64 age;
- int timbreNum;
- dpoly *tmppoly;
-
- CPartialMT32(int partialNum) {
-
- isActive = false;
- pN = partialNum;
-
- /*
- sprintf(buffer, "partial%d.raw",pN);
- fp = fopen(buffer,"wb");
-
- sprintf(buffer, "partial%dx.raw",pN);
- fp2 = fopen(buffer,"wb");
- */
-
-
- };
-
- void startPartial(dpoly *usePoly, patchCache *useCache, dpoly::partialStatus * usePart, CPartialMT32 * pairPart, int mixType, int num, int ownChan, int timNum) {
-
- //LOG_MSG("Starting partial %d for %d", num, ownChan);
- tmppoly = usePoly;
- tcache = useCache;
- partCache = usePart;
- tibrePair = pairPart;
- isActive = true;
- useMix = mixType;
- partNum = num;
- age = 0;
- ownerChan = ownChan;
- alreadyOutputed = false;
- timbreNum = timNum;
- memset(usePart->history,0,sizeof(usePart->history));
-
- }
-
- void stopPartial(void) { isActive = false; }
-
-
+ Bit64s age;
+
+ Partial(Synth *synth);
+
+ int getOwnerPart();
+ bool isActive();
+ void activate(int part);
+ void deactivate(void);
+ void startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPartial);
+ void startDecay(int envnum, Bit32s startval);
+ void startDecayAll();
+ bool shouldReverb();
+
// Returns true only if data written to buffer
- // This function (unline the one below it) returns processed stereo samples
+ // 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(int16 * partialBuf, long length);
-
- // This function produces mono sample output of the specific partial
- void generateSamples(int16 * partialBuf, long length);
+ 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/backends/midi/mt32/partialManager.cpp b/backends/midi/mt32/partialManager.cpp
new file mode 100644
index 0000000000..bbb1c17d71
--- /dev/null
+++ b/backends/midi/mt32/partialManager.cpp
@@ -0,0 +1,269 @@
+/* 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 < MAXPARTIALS; i++)
+ partialTable[i] = new Partial(synth);
+}
+
+PartialManager::~PartialManager(void) {
+ for (int i = 0; i < MAXPARTIALS; i++)
+ delete partialTable[i];
+}
+
+void PartialManager::GetPerPartPartialUsage(int usage[9]) {
+ memset(usage, 0, 9 * sizeof (int));
+ for (int i = 0; i < MAXPARTIALS; i++) {
+ if (partialTable[i]->isActive())
+ usage[partialTable[i]->getOwnerPart()]++;
+ }
+}
+
+void PartialManager::ClearAlreadyOutputed() {
+ for (int i = 0; i < MAXPARTIALS; i++)
+ partialTable[i]->alreadyOutputed = false;
+}
+
+void PartialManager::AgeAll() {
+ for (int i = 0; i < MAXPARTIALS; 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 < MAXPARTIALS; i++) {
+ partialTable[i]->deactivate();
+ }
+}
+
+unsigned int PartialManager::SetReserve(char *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 < MAXPARTIALS; 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 < MAXPARTIALS; 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.params.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.params.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.params.system.reserveSettings[selectPart]) {
+ oldest = -1;
+ oldnum = -1;
+ found = false;
+ for (i = 0; i < MAXPARTIALS; 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 < MAXPARTIALS; 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 < MAXPARTIALS; 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) {
+ Bit64s prior = -1;
+ int priornum = -1;
+
+ for (int i = 0; i < MAXPARTIALS; i++) {
+ if (PartialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
+ /*
+ if (mt32ram.params.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
+ prior = mt32ram.params.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) {
+ Bit64s oldest = -1;
+ Bit64s oldlist = -1;
+ for (int i = 0; i < MAXPARTIALS; i++) {
+ if (partialTable[i]->isActive()) {
+ if (partialTable[i]->getOwnerPart() == partNum) {
+ 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/backends/midi/mt32/partialManager.h b/backends/midi/mt32/partialManager.h
new file mode 100644
index 0000000000..6cc50fecd3
--- /dev/null
+++ b/backends/midi/mt32/partialManager.h
@@ -0,0 +1,56 @@
+/* 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
+
+#define MAXPARTIALS 32
+
+namespace MT32Emu {
+
+class Synth;
+
+class PartialManager {
+private:
+ Synth *synth; // Only used for sending debug output
+
+ Partial *partialTable[MAXPARTIALS];
+ Bit32s PartialReserveTable[MAXPARTIALS];
+ 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(char *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/backends/midi/mt32/structures.h b/backends/midi/mt32/structures.h
index 9fbc8bd627..baa4fb588a 100644
--- a/backends/midi/mt32/structures.h
+++ b/backends/midi/mt32/structures.h
@@ -1,448 +1,60 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
*
- * 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.
+ * 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:
*
- * $Header$
+ * 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 MT32STRUCTURES_H
-#define MT32STRUCTURES_H
+#ifndef MT32EMU_STRUCTURES_H
+#define MT32EMU_STRUCTURES_H
-#include "stdafx.h"
-#include "common/scummsys.h"
+#if (defined (_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && defined(__i386__))
+#define HAVE_X86
+#endif
-#if defined(_MSC_VER)
-typedef unsigned __int64 uint64;
-typedef signed __int64 int64;
+#define MAX_SAMPLE_OUTPUT 4096
+#ifdef HAVE_X86
+#define USE_MMX 1
#else
-typedef unsigned long long uint64;
-typedef signed long long int64;
+#define USE_MMX 0
#endif
-#define INLINE
-
-
-static inline void LOG_MSG(char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- va_end(ap);
- fprintf(stdout, "\n");
- fflush(stdout);
-}
-
-#define ALIGN_PACKED GCC_PACK
-
-#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW32__))
+namespace MT32Emu {
+#ifdef _MSC_VER
+#define ALIGN_PACKED __declspec(align(1))
+typedef unsigned __int64 Bit64u;
+typedef signed __int64 Bit64s;
#else
-
-#ifdef HAVE_X86
-#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 float atti386_iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel)
-{
- return 0;
-}
-
-static inline void atti386_produceOutput1(int tmplen, int16 myvolume, int16 *useBuf, int16 *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");
-}
-
-// FIXME: This is buggy
-static inline void atti386_produceOutput2(uint32 len, int16 *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(int16 * buf1, int16 *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(int16 * buf1, int16 *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(int16 * buf1, int16 *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_PartProductOutput(int quadlen, int16 leftvol, int16 rightvol,
- int16 *partialBuf, int16 *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");
-}
+//#define ALIGN_PACKED __attribute__ ((__packed__))
+#define ALIGN_PACKED __attribute__ ((aligned (1)))
+typedef unsigned long long Bit64u;
+typedef signed long long Bit64s;
#endif
-#endif
-
-#if !defined(__GNUC__)
- #pragma START_PACK_STRUCTS
-#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;
-struct timbreParam {
+#pragma pack(1)
+struct TimbreParam {
struct commonParam {
char name[10];
char pstruct12; // 1&2 0-12 (1-13)
@@ -470,7 +82,7 @@ struct timbreParam {
char time[4]; // 1-100
char level[5]; // 1-100 (-50 - +50)
} ALIGN_PACKED env;
-
+
struct lfoParam {
char rate; // 0-100
char depth; // 0-100
@@ -490,7 +102,7 @@ struct timbreParam {
char envtime[5]; // 1-100
char envlevel[4]; // 1-100
} ALIGN_PACKED tvf;
-
+
struct tvaParam {
char level; // 0-100
char velosens; // 0-100
@@ -503,46 +115,47 @@ struct timbreParam {
char envtime[5]; // 1-100
char envlevel[4]; // 1-100
} ALIGN_PACKED tva;
-
+
} ALIGN_PACKED partial[4];
//char dummy[20];
} ALIGN_PACKED;
-struct memParams {
- struct patchTemp {
- char timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
- char timbreNum; // TIMBRE NUMBER 0-63
- char keyShift; // KEY SHIFT 0-48 (-24 - +24)
- char fineTune; // FINE TUNE 0-100 (-50 - +50)
- char benderRange; // BENDER RANGE 0-24
- char assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
- char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
- char dummy; // (DUMMY)
+struct PatchParam {
+ char timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
+ char timbreNum; // TIMBRE NUMBER 0-63
+ char keyShift; // KEY SHIFT 0-48 (-24 - +24)
+ char fineTune; // FINE TUNE 0-100 (-50 - +50)
+ char benderRange; // BENDER RANGE 0-24
+ char assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
+ char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ char dummy; // (DUMMY)
+} ALIGN_PACKED;
+
+struct MemParams {
+ struct PatchTemp {
+ PatchParam patch;
char outlevel; // OUTPUT LEVEL 0-100
char panpot; // PANPOT 0-14 (R-L)
char dummyv[6];
- } ALIGN_PACKED tmpSettings[8];
- struct ryhTemp {
+ } ALIGN_PACKED patchSettings[8];
+
+ struct RhythmTemp {
char timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF)
char outlevel; // OUTPUT LEVEL 0-100
char panpot; // PANPOT 0-14 (R-L)
char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
- } ALIGN_PACKED rhySettings[64];
+ } ALIGN_PACKED rhythmSettings[64];
- timbreParam timTemp[8];
+ TimbreParam timbreSettings[8];
- struct patchArea {
- char timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
- char timbreNum; // TIMBRE NUMBER 0-63
- char keyShift; // KEY SHIFT 0-48 (-24 - +24)
- char fineTune; // FINE TUNE 0-100 (-50 - +50)
- char benderRange; // BENDER RANGE 0-24
- char assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
- char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
- char dummy; // (DUMMY)
- } ALIGN_PACKED pSettings[128];
- timbreParam patch[192];
- struct systemArea {
+ PatchParam patches[128];
+
+ struct PaddedTimbre {
+ TimbreParam timbre;
+ char padding[10];
+ } ALIGN_PACKED timbres[64 + 64 + 64 + 30]; // Group A, Group B, Memory, Rhythm
+
+ struct SystemArea {
char masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
char reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
char reverbTime; // REVERB TIME 0-7 (1-8)
@@ -551,84 +164,74 @@ struct memParams {
char chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
char masterVol; // MASTER VOLUME 0-100
} ALIGN_PACKED system;
-
} ALIGN_PACKED;
-struct memBanks {
- char pTemp[8][sizeof(memParams::patchTemp)];
- char rTemp[64][sizeof(memParams::ryhTemp)];
- char tTemp[8][sizeof(timbreParam)];
- char patchmemory[128][sizeof(memParams::patchArea)];
- char patchbanks[128][sizeof(timbreParam)];
- char timbrebanks[64][sizeof(timbreParam)];
- char systemBank[sizeof(memParams::systemArea)];
+struct MemBanks {
+ char pTemp[8][sizeof(MemParams::PatchTemp)];
+ char rTemp[64][sizeof(MemParams::RhythmTemp)];
+ char tTemp[8][sizeof(TimbreParam)];
+ char patchBank[128][sizeof(PatchParam)];
+ char timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)];
+ char systemBank[sizeof(MemParams::SystemArea)];
} ALIGN_PACKED;
-struct memAbsolute {
- char mt32memory[sizeof(memBanks)];
+union MT32RAMFormat {
+ MemParams params;
+ MemBanks banks;
+
+ // System memory 10
+
+ // Display 20
+
+ // Reset 7F
+
} ALIGN_PACKED;
-#if !defined(__GNUC__)
- #pragma END_PACK_STRUCTS
-#endif
+#pragma pack()
-struct partialFormat {
- uint32 addr;
- uint16 len;
+struct sampleFormat {
+ Bit32u addr;
+ Bit32u len;
bool loop;
float tune;
- int32 ampval;
+ Bit32s ampval;
};
-struct partialTable {
- uint32 addr;
- uint32 len;
- uint32 pcmnum;
- int32 ampval;
+struct sampleTable {
+ Bit32u addr;
+ Bit32u len;
+ Bit32u pcmnum;
bool loop;
- int32 aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations
+ Bit32s aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations
};
-
-
union soundaddr {
- uint32 pcmabs;
+ Bit32u pcmabs;
struct offsets {
-#if defined(SCUMM_LITTLE_ENDIAN)
- uint16 pcmoffset;
- uint16 pcmplace;
-#else
- uint16 pcmplace;
- uint16 pcmoffset;
-#endif
+ Bit16u pcmoffset;
+ Bit16u pcmplace;
} pcmoffs;
};
-
struct volset {
- int16 leftvol;
- int16 rightvol;
- int16 leftvol2;
- int16 rightvol2;
+ Bit16s leftvol;
+ Bit16s rightvol;
};
-struct patchCache {
- int rawPCM;
- partialTable convPCM;
-
+// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings
+struct PatchCache {
bool playPartial;
- bool usePartial;
bool PCMPartial;
+ int pcm;
char waveform;
int pulsewidth;
int pwsens;
int pitchshift;
int fineshift;
- bool sustain;
int lfodepth;
int lforate;
- uint32 lfoperiod;
+ Bit32u lfoperiod;
int modsense;
int keydir;
@@ -650,121 +253,51 @@ struct patchCache {
int amplevel;
int tvfdepth;
- int prevsample;
-
bool useBender;
- timbreParam::partialParam::envParam pitchEnv;
- timbreParam::partialParam::tvaParam ampEnv;
- timbreParam::partialParam::tvfParam filtEnv;
-
- int32 ampsustain;
- int32 pitchsustain;
- int32 filtsustain;
+ TimbreParam::partialParam::envParam pitchEnv;
+ TimbreParam::partialParam::tvaParam ampEnv;
+ TimbreParam::partialParam::tvfParam filtEnv;
- uint32 partCount;
+ Bit32s ampsustain;
+ Bit32s pitchsustain;
+ Bit32s filtsustain;
- uint8 padding[64]; //Used to pad the patch cache to 4096 bytes. This replaces an imul with a shl 12
+ Bit32u mix;
+ int structurePosition;
+ int structurePair;
+ // The following fields are actually common to all partials in the timbre
+ Bit32u partialCount;
+ bool sustain;
};
+class Partial; // Forward reference for class defined in partial.h
+
struct dpoly {
bool isPlaying;
- bool isDecay;
- bool isActive;
-
- bool partActive[4];
- bool isRy;
- uint32 *bendptr;
- uint32 drumbend;
- int32 *volumeptr;
- volset *pansetptr;
-
- int pcmnum;
+ int note;
int freq;
int freqnum;
int vel;
- uint32 partCount;
-
- soundaddr pcmoff;
- uint32 pcmdelta;
-
-
- struct partialStatus {
- // Note played on keyboard
- int noteval;
- // Keyfollowed note values
- int keyedval;
-
- // Keyfollowed filter values
- int realval;
- int filtval;
- // Keyfollowed filter w/o table
- int filtnoval;
- int pulsewidth;
-
- struct envstatus {
- int32 envpos;
- int32 envstat;
- int32 envbase;
- int32 envdist;
- int32 envsize;
-
- bool sustaining;
- bool decaying;
- bool notdecayed;
- uint32 decay;
- int32 prevlevel;
-
- int32 counter;
- int32 count;
-
- } envs[4];
-
- uint32 lfopos;
- soundaddr partialOff;
- soundaddr wgOff;
-
- uint32 ampEnvCache;
- uint32 pitchEnvCache;
-
- bool isDecayed;
- bool PCMDone;
-
- float history[32];
-
- float pastfilt;
- bool pitchsustain;
- bool playPartial;
- bool usePartial;
-
- int looppos;
- int partNum;
-
- patchCache *tcache;
-
- void * myPart;
-
-
-
- } pStatus[4];
-
-
- int chan;
+ bool reverb;
+ bool isDecay;
- int origpat;
- int drumnum;
-
- int age;
+ const Bit32u *bendptr;
+ Bit32s *volumeptr;
+ volset *pansetptr;
- bool pedalhold;
- bool firstsamp;
+ Partial *partials[4];
- uint32 P1Mix;
- uint32 P2Mix;
+ bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal
bool sustain;
+
+ bool isActive();
+ Bit64s getAge();
};
+}
+
#endif
diff --git a/backends/midi/mt32/synth.cpp b/backends/midi/mt32/synth.cpp
index 138ca29cb7..26448ac3d1 100644
--- a/backends/midi/mt32/synth.cpp
+++ b/backends/midi/mt32/synth.cpp
@@ -1,4446 +1,1277 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
*
- * 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.
+ * 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:
*
- * $Header$
+ * 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 "stdafx.h"
-
-#include "backends/midi/mt32/synth.h"
-#include "backends/midi/mt32/partial.h"
-#include "backends/midi/mt32/freeverb.h"
-#include "common/system.h"
-#include "common/file.h"
-
-#define NOMANSLAND
-
-// **************************
-// CSynth reporting codes
-// **************************
-
-// files missing
-#define ERR_PRESET1 1
-#define ERR_PRESET2 2
-#define ERR_DRUMPAT 3
-#define ERR_PATCHLOG 4
-#define ERR_MT32ROM 5
-
-// HW spec
-#define PRESENT_SSE 6
-#define PRESENT_3DNOW 7
-#define USING_SSE 8
-#define USING_3DNOW 9
-
-// General info
-#define LCD_MESSAGE 10
-#define DEV_RESET 11
-#define DEV_RECONFIG 12
-#define NEW_REVERB_MODE 13
-#define NEW_REVERB_TIME 14
-#define NEW_REVERB_LEVEL 15
-
-#if !defined(__GNUC__)
- #pragma START_PACK_STRUCTS
-#endif
-
-static union mt32ramFormat {
- memParams params;
- memBanks patchabs;
- memAbsolute memabs;
- // System memory 10
- // Display 20
- // Reset 7F
-} ALIGN_PACKED mt32ram, mt32default;
+#define BENCHMARK 0
-#if !defined(__GNUC__)
- #pragma END_PACK_STRUCTS
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#if BENCHMARK > 0
+#include <time.h>
#endif
-int axtoi(char *str) {
- int result = 0;
-
- while (*str) {
- char hex = *str++;
- int digit;
-
- if (hex >= '0' && hex <= '9')
- digit = hex - '0';
- else if (hex >= 'a' && hex <= 'f')
- digit = 10 + hex - 'a';
- else if (hex >= 'A' && hex <= 'F')
- digit = 10 + hex - 'A';
- else
- break;
-
- result = (result << 4) | digit;
- }
-
- return result;
-}
-
-struct FILTER {
- unsigned int length; // size of filter
- float *history; // pointer to history in filter
- float *coef; // pointer to coefficients of filter
-};
-
-#define FILTER_SECTIONS 2 // 2 filter sections for 24 db/oct filter
-
-struct BIQUAD {
- double a0, a1, a2; // numerator coefficients
- double b0, b1, b2; // denominator coefficients
-};
-
-// Filter prototype coefficients, 1 for each filter section
-BIQUAD ProtoCoef[FILTER_SECTIONS];
-
-/**
- * Pre-warp the coefficients of a numerator or denominator. Note that a0 is
- * assumed to be 1, so there is no wrapping of it.
- */
-
-void prewarp(double *a0, double *a1, double *a2, double fc, double fs) {
- double wp;
-
- wp = 2.0 * fs * tan(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)
- *
- * @param a0-a2 s-domain numerator coefficients
- * @param b0-b2 s-domain denominator coefficients
- * @param 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.
- * @param fs sampling rate (Hz)
- * @param coef array of z-domain coefficients to be filled in.
- *
- * @returns On return, set coef z-domain coefficients
- */
-
-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++ = (2. * b0 - 8. * b2 * fs * fs) / bd; // beta1
- *coef++ = (4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd; // beta2
-
- // Nominator
- *coef++ = (2. * a0 - 8. * a2 * fs * fs) / ad; // alpha1
- *coef = (4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad; // alpha2
-}
-
-/**
- * @param a0-a2 numerator coefficients
- * @param b0-b2 denominator coefficients
- * @param fc filter cutoff frequency
- * @param fs sampling rate
- * @param coef pointer to 4 iir coefficients
- */
-
-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(a0, a1, a2, fc, fs);
- prewarp(b0, b1, b2, fc, fs);
- bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef);
-}
-
-#ifdef HAVE_X86
-#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW32__))
-bool DetectSIMD() {
- bool found_simd = false;
-
- _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;
-}
-
-bool Detect3DNow() {
- 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
-bool DetectSIMD() {
- return atti386_DetectSIMD();
-}
-
-bool Detect3DNow() {
- return atti386_Detect3DNow();
-}
-#endif
-#endif
-
-#ifdef NOMANSLAND
-
-// #define SETRATE 32000
-#define SETRATE myProp.SampleRate
-// #define SETRATE 44100
-
-// Used to regenerate waveform file after sampling rate change
-#define MAKEWAVES 0
+#include "mt32emu.h"
+// Debugging stuff
// Used to dump drum patches to syx file for viewing
#define DUMPDRUMS 0
-#define USEREVERB 1
-
-// Debuging stuff
-// Shows the instruments played
-#define DISPLAYINSTR 0
-// Shows number of partials MT-32 is playing
-#define MONITORPARTIALS 1
-// Dump syx file of temp tibres right before reset
-#define SAVECUSTOM 0
-
-// Constant tuning for now
-#define TUNING 440.0
-#define SAMPLETUNING 207.64
-#define MIDDLEC 60
-
-#define ROMSIZE 512*1024
-#define PCMSIZE ROMSIZE/2
-#define GRAN 512
-#define LN 2.30258509
-
-#define MAXPOLY 64
-#define MAXPARTIALS 32
-
-// Reverb room sizes (in metres)
-
-#define REV_ROOMSIZE 2.50f
-#define REV_HALLSIZE 3.60f
-#define REV_PLATESIZE 1.50f
-#define REV_TAPSIZE 1.0f
-
-// Reverb t60 coeff
-
-#define REV_ROOMT60 ( REV_ROOMSIZE * REV_ROOMSIZE * REV_ROOMSIZE ) / 5
-#define REV_HALLT60 ( REV_HALLSIZE * REV_HALLSIZE * REV_HALLSIZE ) / 5
-#define REV_PLATET60 ( REV_PLATESIZE * REV_PLATESIZE * REV_PLATESIZE ) / 7
-#define REV_TAPT60 ( REV_TAPSIZE * REV_TAPSIZE * REV_TAPSIZE ) / 1
-
-// #define HLRATIO 2.0f
-
#define SYSEX_SIZE 512
-// These are all the filters I tried without much success
-
-int16 Moog1(int16 wg, float *hist, float usefilt, float res) {
- float f, p, q; // filter coefficients
- float t1, t2; // temporary buffers
-
- // Set coefficients given frequency & resonance [0.0...1.0]
-
- float frequency = usefilt;
- float in = (float)wg / 32767.0;
- float resonance = res / 31.0;
- resonance = resonance * resonance;
-
- q = 1.0f - frequency;
- //p = frequency + 0.8f * frequency * q;
- p = frequency + 0.8f * frequency * q;
-
- f = p + p - 1.0f;
- q = resonance * (1.0f + 0.5f * q * (1.0f - q + 5.6f * q * q));
-
- // Filter (in [-1.0...+1.0])
-
- // feedback
- in -= q * hist[4];
- t1 = hist[1];
- hist[1] = (in + hist[0]) * p - hist[1] * f;
- t2 = hist[2];
- hist[2] = (hist[1] + t1) * p - hist[2] * f;
- t1 = hist[3];
- hist[3] = (hist[2] + t2) * p - hist[3] * f;
- hist[4] = (hist[3] + t1) * p - hist[4] * f;
-
- // clipping
- hist[4] = hist[4] - hist[4] * hist[4] * hist[4] * 0.166667f;
- hist[0] = in;
- //LOG_MSG("In %d Hist: %f", wg, hist[4]*32767);
-
- return (int16)(hist[4] * 32767.0);
-}
-
-int16 Moog2(int16 wg, float *hist, float usefilt, float resonance) {
- float res = resonance / 30.0;
- double f = usefilt;
- double invf = 1.0 - f;
- double fb = res * (1.0 - 0.15 * f * f);
- float input = (float)wg / 32767.0;
-
- input -= hist[4] * fb;
- input *= 0.35013 * (f*f)*(f*f);
- hist[1] = input + 0.3 * hist[5] + (invf) * hist[1]; // Pole 1
- hist[5] = input;
- hist[2] = hist[1] + 0.3 * hist[6] + (invf) * hist[2]; // Pole 2
- hist[6] = hist[1];
- hist[3] = hist[2] + 0.3 * hist[7] + (invf) * hist[3]; // Pole 3
- hist[7] = hist[2];
- hist[4] = hist[3] + 0.3 * hist[0] + (invf) * hist[4]; // Pole 4
- hist[0] = hist[3];
-
- return (int16)(hist[4] * 32767.0);
-}
-
-int16 simpleLowpass(int16 wg, float *hist, float usefilt, float resonance) {
- float in = (float)wg / 32767.0;
- float res_lp = resonance / 31.0;
- res_lp = res_lp * res_lp;
- float cut_lp = usefilt;
- float n1, n2, fb_lp;
-
- n1 = hist[0];
- n2 = hist[1];
-
- fb_lp = res_lp + res_lp / (1 - cut_lp);
- n1 = n1 + cut_lp * (in - n1 + fb_lp * (n1 - n2));
- n2 = n2 + cut_lp * (n1 - n2);
-
- hist[0] = n1;
- hist[1] = n2;
-
- return (int)(n2 * 32767.0);
-}
-
-/* Reverb stuff */
-
-#define NUM_COMBS 6
+namespace MT32Emu {
-struct LOWPASS_STATE {
- float coef;
- float lastval;
-};
+iir_filter_type usefilter;
-struct COMB_STATE {
- float tau;
- float g;
- float gsqu;
- float *delbuf;
- int bufsiz;
- int bufpos;
+static const Bit8u InitPatches[8] = {
+ 68, 48, 95, 78, 41, 3, 110, 122
};
-struct ST_REVERB {
- int lastsamp;
- int cursamp;
- int done;
- LOWPASS_STATE lowpass[NUM_COMBS];
- COMB_STATE comb[NUM_COMBS];
- COMB_STATE allpass[2];
+// Maps MIDI channel numbers to MT-32 parts (not to be confused with "partials")
+// This is the default (FIXME: the mapping from 11->9 is undocumented, is this correct?):
+static const Bit8s InitChanTable[16] = {
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, -1, -1, -1, -1, -1
};
+// This alternative configuration can be selected by holding "Master Volume"
+// and pressing "PART button 1" on the real MT-32's frontpanel.
+//static const Bit8s InitChanTable[16] = {
+// 0, 1, 2, 3, 4, 5, 6, 7, -1, 8, -1, -1, -1, -1, -1, -1
+//};
-class Reverb {
-private:
- ST_REVERB *revstate;
- int SR;
-
-public:
- Reverb(float t60, float hlratio, float dur, float hall_f, int smpr);
- ~Reverb();
-
- void run(float *lchan, float *rchan, float revfrac);
-
- float lowpass(float input, LOWPASS_STATE *state);
-
- float lpcomb(float input, LOWPASS_STATE *lpstate, COMB_STATE *cstate);
- float allpassfilt(float input, COMB_STATE *state);
-};
-
-/**
- * @param t60 reverb time
- * @param hlratio ratio of low freq t60 to high freq t60
- * @param dur duration of event/dealloc. on last samp
- * @param hall_fact mult. factor for delay times
- * @param revstate running values for event reverb
- */
-
-Reverb::Reverb(float t60, float hlratio, float dur, float hall_fact, int sampling_rate) {
- revstate = new ST_REVERB;
- SR = sampling_rate;
- int i;
- float glow[NUM_COMBS], ghi[NUM_COMBS];
- // initialize sample counter and compute last sample
- revstate->cursamp=0;
- revstate->lastsamp = (int)(dur * (float)SR);
- revstate->done=0;
-
- // ALLPASS INITIALIZATIONS
- revstate->allpass[0].tau = .006 * hall_fact;
- revstate->allpass[1].tau = .0065 * hall_fact;
-
- // allocate allpass delay buffers and head/tail ptr.
- for (i = 0; i < 2; i++) {
- revstate->allpass[i].bufsiz = (int) (revstate->allpass[i].tau * SR + .5);
- revstate->allpass[i].delbuf = new float[revstate->allpass[i].bufsiz];
- memset(revstate->allpass[i].delbuf, 0, revstate->allpass[i].bufsiz * sizeof(float));
- revstate->allpass[i].bufpos = -1;
- }
-
- revstate->allpass[0].g = .71f;
- revstate->allpass[1].g = .7f;
-
- revstate->allpass[0].gsqu = revstate->allpass[0].g * revstate->allpass[0].g;
- revstate->allpass[1].gsqu = revstate->allpass[1].g * revstate->allpass[1].g;
-
- // COMB AND IIR LOWPASS FILTER INITIALIZATIONS
-
- revstate->comb[0].tau = .0050 * hall_fact;
- revstate->comb[1].tau = .0068 * hall_fact;
- revstate->comb[2].tau = .0056 * hall_fact;
- revstate->comb[3].tau = .0072 * hall_fact;
- revstate->comb[4].tau = .0061 * hall_fact;
- revstate->comb[5].tau = .0078 * hall_fact;
-
- // allocate comb delay buffers and head/tail ptr.
- for (i = 0; i < NUM_COMBS; i++) {
- revstate->comb[i].bufsiz = (int)(revstate->comb[i].tau * SR + .5);
-
- revstate->comb[i].delbuf = new float[revstate->comb[i].bufsiz];
- memset(revstate->comb[i].delbuf, 0, revstate->comb[i].bufsiz * sizeof(float));
-
- revstate->comb[i].bufpos = -1;
-
- revstate->lowpass[i].lastval = 0.;
- }
-
- // if hlratio set by user, set various values
- // else, use default g's and coef's
- if (hlratio != 0.) {
- for (i = 0; i < NUM_COMBS; i++) {
-
- // compute reverb attenuation factor for hi and low
- // frequency reverberation times
- glow[i] = pow(10.,(-3. * revstate->comb[i].tau) / t60);
- ghi[i] = pow(10.,(-3. * revstate->comb[i].tau) / ( t60 * hlratio));
-
- // compute recursive lowpass factor and comb
- // attenuation factor to produce the correct
- // reverberation time for both hi and low frequencies
- revstate->lowpass[i].coef = (glow[i] - ghi[i]) / (glow[i] + ghi[i]);
- revstate->comb[i].g = glow[i] * (1. - revstate->lowpass[i].coef);
- }
- } else {
- revstate->lowpass[0].coef = .24f;
- revstate->lowpass[1].coef = .26f;
- revstate->lowpass[2].coef = .28f;
- revstate->lowpass[3].coef = .29f;
- revstate->lowpass[4].coef = .30f;
- revstate->lowpass[5].coef = .32f;
-
- for (i = 0; i < 6; i++) {
- // compute reverb attenuation factor and comb
- // attenuation factor based on default coef
- glow[i] = pow(10., (-3. * revstate->comb[i].tau) / t60);
- revstate->comb[i].g = glow[i] * (1. - revstate->lowpass[i].coef);
- }
- }
-}
-
-Reverb:: ~Reverb() {
- int i;
-
- for (i = 0; i < NUM_COMBS; i++)
- delete[] revstate->comb[i].delbuf;
- for (i = 0; i < 2; i++)
- delete[] revstate->allpass[i].delbuf;
- delete revstate;
-}
-
-/**
- * @param lchan non-reverberated input sample
- * @param rchan non-reverberated input sample
- * @param revfrac percent of output to be reverberated
- */
-
-INLINE void Reverb::run(float *lchan, float *rchan, float revfrac) {
- int i;
- float lchanrev, rchanrev, tot = 0;
-
- if (revstate->done) {
- *lchan = 0.0;
- *rchan = 0.0;
- return;
- }
-
- for (i = 0; i < NUM_COMBS; i++)
- tot = tot + lpcomb((*lchan) + (*rchan), &(revstate->lowpass[i]), &(revstate->comb[i]));
-
- tot = tot / (float)NUM_COMBS;
-
- lchanrev = allpassfilt(tot * .7, &(revstate->allpass[0]));
- rchanrev = allpassfilt(tot * .7, &(revstate->allpass[1]));
-
- if (revstate->cursamp == revstate->lastsamp) {
- for (i = 0; i < NUM_COMBS; i++)
- delete[] revstate->comb[i].delbuf;
- for (i = 0; i < 2; i++)
- delete[] revstate->allpass[i].delbuf;
- revstate->done = 1;
- }
-
- revstate->cursamp++;
-
- *lchan = lchanrev * revfrac + (*lchan) * (1. - revfrac);
- *rchan = rchanrev * revfrac + (*rchan) * (1. - revfrac);
-}
-
-INLINE float Reverb::lowpass(float input, LOWPASS_STATE *state) {
- // simple IIR lowpass filter algorithm
- // y(n) = x(n) + coef * y(n - 1)
- state->lastval = (input + state->coef * state->lastval);
- return state->lastval;
-}
-
-INLINE float Reverb::lpcomb(float input, LOWPASS_STATE *lpstate, COMB_STATE *cstate) {
- float temp;
-
- // move head-tail pointer in circular queue
- cstate->bufpos = (cstate->bufpos + 1) % cstate->bufsiz;
-
- // pop circular queue
- temp = cstate->delbuf[cstate->bufpos];
-
- // add new value to end of queue
- lpstate->lastval = (cstate->delbuf[cstate->bufpos] + lpstate->coef * lpstate->lastval);
-
- cstate->delbuf[cstate->bufpos] = input + cstate->g * lpstate->lastval;
-
- // return popped value
- return temp;
-}
-
-INLINE float Reverb::allpassfilt(float input, COMB_STATE* state) {
- float temp;
-
- // move head-tail pointer in circular queue
- state->bufpos = (state->bufpos + 1) % state->bufsiz;
-
- // pop circular queue
- temp = state->delbuf[state->bufpos];
-
- // add new value to end of queue
- state->delbuf[state->bufpos] = input + state->g * state->delbuf[state->bufpos];
-
- // return a sum of the current in with the delay out
- return -1. * state->g * input + (1. - state->gsqu) * temp;
-}
-
-// End reverb stuff
-
-// Begin filter stuff
-
-void InitFilter(float fs, float fc, float *icoeff, float Q, float resfac) {
- float *coef;
- unsigned nInd;
- double a0, a1, a2, b0, b1, b2;
- double k; // overall gain factor
-
- // Section 1
- ProtoCoef[0].a0 = 1.0;
- ProtoCoef[0].a1 = 0;
- ProtoCoef[0].a2 = 0;
- ProtoCoef[0].b0 = 1.0;
- ProtoCoef[0].b1 = 0.765367;
- ProtoCoef[0].b2 = 1.0;
-
- // Section 2
- ProtoCoef[1].a0 = 1.0;
- ProtoCoef[1].a1 = 0;
- ProtoCoef[1].a2 = 0;
- ProtoCoef[1].b0 = 1.0;
- ProtoCoef[1].b1 = 1.847759;
- ProtoCoef[1].b2 = 1.0;
-
- k = 1.5; // Set overall filter gain
- coef = icoeff + 1; // Skip k, or gain
-
- for (nInd = 0; nInd < 2; nInd++) {
- a0 = ProtoCoef[nInd].a0;
- a1 = ProtoCoef[nInd].a1;
- a2 = ProtoCoef[nInd].a2;
-
- b0 = ProtoCoef[nInd].b0;
- b1 = ProtoCoef[nInd].b1 / Q; // Divide by resonance or Q
- b2 = ProtoCoef[nInd].b2;
-
- szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
- coef += 4; // Point to next filter section
- }
- icoeff[0] = k;
-}
-
-#if FILTER_FLOAT == 1
-
-iir_filter_type usefilter;
-
-#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW32__))
-
-float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
- float *hist2_ptr;
- float output;
-
- hist2_ptr = hist1_ptr + 1; // next history
-
- // 1st number of coefficients array is overall input scale factor, or
- // filter gain
- output = input * (*coef_ptr++);
-
- __asm {
- movss xmm1, output
-
- mov eax, coef_ptr
- movups xmm2, [eax]
-
- mov eax, hist1_ptr
- movlps xmm3, [eax]
- shufps xmm3, xmm3, 44h
- // hist2_ptr, hist1_ptr, hist2_ptr, 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
- // hist2_ptr, hist1_ptr, hist2_ptr, 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
- }
-
- output *= ResonInv[revLevel];
-
- return output;
-}
-
-float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
- float *hist2_ptr;
- float output;
- float tmp;
-
- hist2_ptr = hist1_ptr + 1; // next history
-
- // 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.
-
- __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
+static int axtoi(char *str) {
+ int result = 0;
+ while (*str) {
+ char ch = *str++;
+ if (ch >= '0' && ch <= '9')
+ ch -= '0';
+ else if (ch >= 'a' && ch <= 'f')
+ ch = ch + 10 - 'a';
+ else if (ch >= 'A' && ch <= 'F')
+ ch = ch + 10 - 'A';
+ else
+ break;
+ result = (result << 4) | ch;
}
-
- output *= ResonInv[revLevel];
-
- return output;
-}
-
-#elif defined(HAVE_X86)
-
-float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
- float *hist2_ptr;
- float output;
-
- 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 = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr);
- output *= ResonInv[revLevel];
-
- return output;
-}
-
-float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
- float *hist2_ptr;
- float output;
-
- 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 = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr);
- output *= ResonInv[revLevel];
-
- return output;
+ return result;
}
-#endif
-
float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
float *hist2_ptr;
- float output, new_hist;
+ float output,new_hist;
- hist2_ptr = hist1_ptr + 1; // next history
+ hist2_ptr = hist1_ptr + 1; // next history
- // 1st number of coefficients array is overall input scale factor, or
- // filter gain
+ // 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
+ new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
output = new_hist + *hist1_ptr * (*coef_ptr++);
- output = output + *hist2_ptr * (*coef_ptr++); // zeros
+ 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
+ new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
output = new_hist + *hist1_ptr * (*coef_ptr++);
- output = output + *hist2_ptr * (*coef_ptr++); // zeros
-
+ output = output + *hist2_ptr * (*coef_ptr++); // zeros
+
*hist2_ptr++ = *hist1_ptr;
*hist1_ptr++ = new_hist;
output *= ResonInv[revLevel];
- return output;
+ return(output);
}
-#endif
-
-#if FILTER_64BIT == 1
-
-// 64-bit version
-long iir_filter(long input, int64 *hist1_ptr, int64 *coef_ptr) {
- int64 *hist2_ptr;
- int64 output, new_hist, history1, history2;
-
- hist2_ptr = hist1_ptr + 1; // next history
-
- // 1st number of coefficients array is overall input scale factor, or
- // filter gain
- output = input * (*coef_ptr++);
-
- for (int i = 0 ; i < 2; i++) {
- history1 = *hist1_ptr; // history values
- history2 = *hist2_ptr;
-
- output = output - ((history1 * (*coef_ptr++)) >> 20);
- new_hist = output - ((history2 * (*coef_ptr++)) >> 20); // poles
+Synth::Synth() {
+ isOpen = false;
+ reverbModel = NULL;
+ partialManager = NULL;
+ memset(waveforms, 0, sizeof(waveforms));
+ memset(parts, 0, sizeof(parts));
+}
- output = new_hist + ((history1 * (*coef_ptr++)) >> 20);
- output = output + ((history2 * (*coef_ptr++)) >> 20); // zeros
+Synth::~Synth() {
+ close(); // Make sure we're closed and everything is freed
+}
- *hist2_ptr++ = *hist1_ptr;
- *hist1_ptr++ = new_hist;
- hist1_ptr++;
- hist2_ptr++;
+void Synth::report(ReportType type, void *data) {
+ if (myProp.report != NULL) {
+ myProp.report(myProp.userData, type, data);
}
-
- return output >> 20;
}
-#endif
-
-#if FILTER_INT == 1
-
-long iir_filter(long input, signed long *hist1_ptr, signed long *coef_ptr) {
- signed long *hist2_ptr;
- signed long output, new_hist, history1, history2;
-
- hist2_ptr = hist1_ptr + 1; // next history
-
- // 1st number of coefficients array is overall input scale factor, or
- // filter gain
- output = input * (*coef_ptr++);
-
- for (int i = 0 ; i < 2; i++) {
- history1 = *hist1_ptr; // history values
- history2 = *hist2_ptr;
-
- output = output - ((history1 * (*coef_ptr++)) >> 10);
- new_hist = output - ((history2 * (*coef_ptr++))>>10); // poles
-
- output = new_hist + ((history1 * (*coef_ptr++)) >> 10);
- output = output + ((history2 * (*coef_ptr++)) >> 10); // zeros
-
- *hist2_ptr++ = *hist1_ptr;
- *hist1_ptr++ = new_hist;
- hist1_ptr++;
- hist2_ptr++;
+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");
}
-
- return output >> 10;
+ va_end(ap);
}
-#endif
-
-// End filter stuff
-
-partialFormat PCM[54];
-partialTable PCMList[128];
-uint32 PCMReassign[55];
-int32 PCMLoopTable[55];
-
-timbreParam drums[30];
-
-int16 romfile[PCMSIZE+GRAN]; // 256K
-static int16 chantable[32]; // 64 bytes
-static int16 miditable[9]; // 18 bytes
-
-static CPartialMT32 *partTable[MAXPARTIALS];
-static int32 PartialReserveTable[32];
+void Synth::initReverb(char newRevMode, char newRevTime) {
+ // 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();
-// For debuging partial allocation
-//static FILE *pInfo;
-struct partUsage {
- int active[32];
- int assign[32];
- int owner[32];
- int status[32];
-};
-
-static int32 activeChannels;
-
-// Some optimization stuff
-int32 divtable[256]; // 1K
-int32 smalldivtable[256]; // 1K
-static int16 freqtable[256]; // 512 bytes
-static uint32 sqrtable[101]; // 404 bytes
-static int32 keytable[256]; // 1K
-static uint32 wavtable[256]; // 1K
-uint32 wavtabler[64][256]; // 64K
-uint32 looptabler[16][16][256]; // 256K
-static uint32 drumdelta[256]; // 1K
-int16 sintable[65536]; // 128K
-static int32 ptable[101]; // 404 bytes
-static int32 lfotable[101]; // 404 bytes
-int32 penvtable[16][128]; // 8K
-static int32 fildeptable[5][128]; // 3K
-static int32 timekeytable[5][128]; // 3K
-static int32 filveltable[128][128]; // 64K
-static int32 veltkeytable[5][128]; // 3K
-int32 pulsetable[101]; // 400 bytes
-int32 pulseoffset[101]; // 400 bytes
-int32 sawtable[128][128]; // 64K
-static int32 restable[201]; // 804 bytes
-//static int32 biastable[13]; // 56 bytes
-static int32 ampbiastable[16][128]; // 8K
-static int32 fbiastable[16][128]; // 8K
-static int filttable[2][128][256]; // 256K
-static int nfilttable[128][128][128]; // 64K
-float filtcoeff[FILTERGRAN][32][16]; // 512K - hmmm
-#if FILTER_64BIT == 1
-static int64 filtcoefffix[FILTERGRAN][32][16];
-#endif
-#if FILTER_INT == 1
-static int32 filtcoefffix[FILTERGRAN][32][16];
-#endif
-static float revtable[8]; // 16 bytes
-static int32 finetable[201]; // 804 bytes
-uint32 lfoptable[101][128]; // 32K
-int32 ampveltable[128][64]; // 32K
-int32 pwveltable[15][128];
-static int32 envtimetable[101]; // 404 bytes
-static int32 decaytimetable[101]; // 404 bytes
-static int32 lasttimetable[101]; // 404 bytes
-int32 amptable[129]; // 516 bytes
-static int32 voltable[129]; // 516 bytes
-static float padjtable[51]; // 204 bytes
-static int32 bendtable[49]; // 195 bytes
-float ResonFactor[32];
-float ResonInv[32];
-
-int16 smallnoise[441]; // 4410 bytes at 44Khz
-int32 samplepos = 0;
-
-int16* waveforms[4][256]; // 2K
-uint32 waveformsize[4][256];
-int16 tmpforms[4][65536]; // 128K
-int16 finalforms[4][8192]; // 64K
-
-// Corresponding drum patches as matched to keyboard
-int8 DrumTable[42] = {
- 0, 0, 10, 1, 11, 5, 4, 6, 4, 29, 3, 7, 3, 2, 8, 2, 9, -1, -1, 22,
- -1, 12, -1, -1, -1, 18, 19, 13, 14, 15, 16, 17, 20, 21, 27, 24,
- 26, 25, 28, -1, 23, -1 };
-
-// Pan-pot position of drums
-int16 drmPanTable[42] = {
- 64, 64, 72, 64, 48, 72, 24, 72, 24, 72, 48, 72, 48, 96, 72, 96, 48, 1, 1, 40,
- 1, 64, 1, 1, 1, 104, 88, 48, 40, 32, 64, 80, 104 , 104, 40, 88,
- 40, 40, 32, 1, 16, 1 };
-
-uint8 PartialStruct[13] = {
- 0, 0, 2, 2, 1, 3,
- 3, 0, 3, 0, 2, 1, 3 };
-
-uint8 PartMixStruct[13] = {
- 0, 1, 0, 1, 1, 0,
- 1, 3, 3, 2, 2, 2, 2 };
-
-uint8 InitInstr[8] = {
- 68, 48, 95, 78, 41, 3, 110, 122};
-
-int8 LoopPatterns[16][16] = {
- { 2,3,4,5,6,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 8,9,10,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1 },
- { 17,18,19,20,21,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 22,23,24,25,26,27,28,29,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 30,31,32,33,34,35,36,37,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 45,46,47,48,49,50,51,52,53,-1,-1,-1,-1,-1,-1,-1 },
- { 15,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 30,35,32,33,34,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }
-};
-
-
-int32 LoopPatternTuning[16][16] = {
- { 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1 },
- { 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1 },
- { 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1 },
- { 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1 },
- { 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A, 0x1294A,-1,-1,-1,-1,-1,-1,-1 },
- { 0x2590B,0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 0x1294A,0x1294A,0x1294A,0x1294A,0x1294A,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { 0x1294A,0x1294A,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 },
- { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }
-
-};
-
-
-// These are division constants for the TVF depth key follow
-uint32 depexp[5] = {3000,950,485,255,138};
-
-//Amplitude time velocity follow exponential coefficients
-double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
-double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};
-
-//Envelope time keyfollow exponential coefficients
-double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215};
-double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067};
-
-// This caches the drum information
-patchCache drumCache[30][4];
-int drumPan[30][2];
-
-bool isEnabled;
-
-
-Reverb *myReverb;
-revmodel *newReverb;
-bool usingSIMD;
-
-int16 mastervolume;
-
-uint32 curRevMode;
-uint32 curRevTime;
-uint32 curRevLevel;
-
-uint32 partialsPlayed; // Variable containing the whole count of partials played
-uint32 avgPartials; // Tally of average number of partials a second
-int32 partialChan[9]; // The count of partials played per channel
-
-#if SAVECUSTOM == 1
-uint32 filenum = 0;
-#endif
-
-/*
-t60 = reverb time
-hlratio = ratio of low freq t60 to high freq t60
-dur = duration of event/dealloc. on last samp
-hall_fact= mult. factor for delay times
-revstate = running values for event reverb
-*/
-
-// t60 follows standard sabine equation
-// t60 = .049 * (V / A)
-// Where V = is the volume of the enclosure in cubic feet
-// And A is the absorbtion of the room in square feet
-// Using metric measurements (metres) the .049 is replaced with 0.161
-
-
-
-void InitReverb(uint32 newRevMode, uint32 newRevTime, uint32 sampRate) {
- if(newReverb != NULL) delete newReverb;
- newReverb = new revmodel();
-
- /*
- if(myReverb != NULL) delete myReverb;
-
- float t60;
- switch(newRevMode) {
- case 0:
- t60 = .161f * REV_ROOMT60;
- break;
- case 1:
- t60 = .161f * REV_HALLT60;
- break;
- case 2:
- t60 = .161f * REV_PLATET60;
- break;
- case 3:
- t60 = .161f * REV_TAPT60;
- break;
- default:
- t60 = .161f * REV_ROOMT60;
- break;
- }
-
- t60 = t60 * 0.625;
- myReverb = new Reverb(t60,HLRATIO,sampRate/(8/(newRevTime+1)),(newRevTime+1),sampRate);
- */
curRevTime = newRevTime;
curRevMode = newRevMode;
switch(newRevMode) {
case 0:
- newReverb->setroomsize((float).1);
- newReverb->setdamp((float).75);
+ reverbModel->setroomsize(.1f);
+ reverbModel->setdamp(.75f);
break;
case 1:
- newReverb->setroomsize((float).5);
- newReverb->setdamp((float).5);
+ reverbModel->setroomsize(.5f);
+ reverbModel->setdamp(.5f);
break;
case 2:
- newReverb->setroomsize((float).5);
- newReverb->setdamp((float).1);
+ reverbModel->setroomsize(.5f);
+ reverbModel->setdamp(.1f);
break;
case 3:
- newReverb->setroomsize((float)1);
- newReverb->setdamp((float).75);
+ reverbModel->setroomsize(1.0f);
+ reverbModel->setdamp(.75f);
break;
default:
- newReverb->setroomsize((float).1);
- newReverb->setdamp((float).5);
+ reverbModel->setroomsize(.1f);
+ reverbModel->setdamp(.5f);
break;
}
- newReverb->setdry(1);
- newReverb->setwet((float)mt32ram.params.system.reverbLevel / 8.0);
- newReverb->setwidth((float)curRevTime / 8.0);
-
-
-}
-
-class MidiChannel {
-public:
- bool isPlaying;
-
- volset volumesetting;
-
- int reverbat, reverbleft, reverbright;
- int panpot;
-
- patchCache pcache[4];
-
- uint32 bend;
- int32 volume;
-
- dpoly polyTable[MAXPOLY];
-
-
-private:
- bool isRy;
- int sampRate;
- int freq;
- int channum;
- int partialCount;
- int velocity;
- long sndoff;
- int octave;
- int note;
-
- int patch;
- char currentInstr[20];
-
- int keyshift;
- int assignmode;
- int storedpatch;
- bool sustain;
- bool init;
-
- uint32 P1Mix;
- uint32 P2Mix;
-
- bool holdpedal;
-
-
-public:
- MidiChannel(int samp, int cnum);
- //short getSample(short *lspecial, short *rspecial);
- void PlayPoly(int m, int f,int vel);
- void PlayDrum(int m, int f,int vel);
- void InitKeyFollow(dpoly *tpoly);
- void PlayNote(int f, int vel);
- void StopNote(int f, int vel);
- void AllStop();
- void SetVolume(int vol);
- void SetPan(int vol);
- void SetBend(int vol);
- void SetModulation(int vol);
- void SetPatch(int patchnum,int drumNum);
- void SetHoldPedal(bool pedalval);
- void StopPedalHold();
- void RefreshPatch();
- void CheckNoteList();
-
- int FixKeyfollow(int srckey, int *dir);
- int FixBiaslevel(int srcpnt, int *dir);
-
- //int32 getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly, bool inDecay);
- //int32 getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly, bool inDecay);
- //int32 getFiltEnvelope(int16 wg, dpoly::partialStatus *pStat, dpoly *poly, bool inDecay);
-
- //void StartDecay(int envnum, int32 startval, dpoly::partialStatus *pStat, dpoly *poly);
-
-};
-
-void MidiChannel::SetHoldPedal(bool pedalval) {
- holdpedal = pedalval;
+ reverbModel->setdry(1);
+ reverbModel->setwet((float)mt32ram.params.system.reverbLevel / 8.0f);
+ reverbModel->setwidth((float)curRevTime / 8.0f);
}
-void MidiChannel::SetBend(int vol) {
-
- //int tmpbend = ((vol - 0x2000) * (int)mt32ram.params.pSettings[storedpatch].benderRange) >> 13;
- //bend = bendtable[tmpbend+24];
-
- float bend_range = (float)mt32ram.params.pSettings[storedpatch].benderRange / 24;
- bend = 4096 + (int)((float)(vol - 8192) * bend_range);
-}
-
-void MidiChannel::SetModulation(int vol) {
-
- // Just a bloody guess, as always, before I get things figured out
- int t;
- for(t=0;t<4;t++) {
- if(pcache[t].playPartial) {
- int newrate = (pcache[t].modsense * vol) >> 7;
- //pcache[t].lfoperiod = lfotable[newrate];
- pcache[t].lfodepth = newrate;
- }
+File *Synth::openFile(const char *filename, File::OpenMode mode) {
+ if (myProp.openFile != NULL) {
+ return myProp.openFile(myProp.userData, filename, mode);
}
-
-}
-
-INLINE void StartDecay(int envnum, int32 startval, dpoly::partialStatus *pStat, dpoly *poly) {
-
- patchCache *tcache = pStat->tcache;
- dpoly::partialStatus::envstatus *tStat = &pStat->envs[envnum];
-
- tStat->sustaining = false;
- tStat->decaying = true;
- tStat->envpos = 0;
- tStat->envbase = startval;
-
- switch(envnum) {
- case AMPENV:
- tStat->envsize = (decaytimetable[(int)tcache->ampEnv.envtime[4]] * timekeytable[(int)tcache->ampEnv.envtkf][poly->freqnum]) >> 8;
- tStat->envdist = -startval;
- break;
- case FILTENV:
- tStat->envsize = (decaytimetable[(int)tcache->filtEnv.envtime[4]] * timekeytable[(int)tcache->filtEnv.envtkf][poly->freqnum]) >> 8;
- tStat->envdist = -startval;
- break;
- case PITCHENV:
- tStat->envsize = (decaytimetable[(int)tcache->pitchEnv.time[3]] * timekeytable[(int)tcache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8 ;
- tStat->envdist = tcache->pitchEnv.level[4] - startval;
- break;
- default:
- break;
+ char pathBuf[2048];
+ if (myProp.baseDir != NULL) {
+ strcpy(&pathBuf[0], myProp.baseDir);
+ strcat(&pathBuf[0], filename);
+ filename = pathBuf;
}
- tStat->envsize++;
-
-
+ ANSIFile *file = new ANSIFile();
+ if (!file->open(filename, mode)) {
+ delete file;
+ return NULL;
+ }
+ return file;
}
-
-
-
-
-INLINE int32 getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly) {
- int32 tc;
-
- patchCache *tcache = pStat->tcache;
- dpoly::partialStatus::envstatus *tStat = &pStat->envs[AMPENV];
-
- if(!pStat->playPartial) return 0;
-
- if(tStat->decaying) {
-
- if(pStat->isDecayed) {
- pStat->playPartial = false;
-
- tc = 0;
- } else {
- tc = tStat->envbase;
- tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
-
- // TRISTAN
- if (tc < 0)
- tc = 0;
-
- if((tStat->envpos >= tStat->envsize) || (tc == 0)){
- pStat->PCMDone = true;
- pStat->isDecayed = true;
- pStat->playPartial = false;
- }
- }
-
+void Synth::closeFile(File *file) {
+ if (myProp.closeFile != NULL) {
+ myProp.closeFile(myProp.userData, file);
} else {
-
- if((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
- if(tStat->envstat==-1) {
- tStat->envbase = 0;
- } else {
- tStat->envbase = tcache->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)tcache->ampEnv.envtime[tStat->envstat]] * veltkeytable[(int)tcache->ampEnv.envvkf][poly->vel]) >> 8;
- //LOG_MSG("Envstat %d, size %d, %d %d", tStat->envstat, tStat->envsize, tcache->ampEnv.envtime[tStat->envstat], veltkeytable[tcache->ampEnv.envvkf][poly->vel]);
- break;
- case 3:
- // Final attack envelope uses same time table as the decay
- //tStat->envsize = decaytimetable[tcache->ampEnv.envtime[tStat->envstat]];
- tStat->envsize = lasttimetable[(int)tcache->ampEnv.envtime[(int)tStat->envstat]];
- //LOG_MSG("Envstat %d, size %d", tStat->envstat, tStat->envsize);
- break;
- case 4:
- //LOG_MSG("Envstat %d, size %d", tStat->envstat, tStat->envsize);
- tc =tcache->ampsustain;
- if(!poly->sustain)
- StartDecay(AMPENV, tc, pStat, poly);
- else
- tStat->sustaining = true;
-
- goto PastCalc;
- default:
- //Spot for timekey follow
- //Only used in subsquent envelope parameters, including the decay
- tStat->envsize = (envtimetable[(int)tcache->ampEnv.envtime[tStat->envstat]] * timekeytable[(int)tcache->ampEnv.envtkf][poly->freqnum]) >> 8;
-
- //LOG_MSG("Envstat %d, size %d", tStat->envstat, tStat->envsize);
- break;
- }
-
- tStat->envsize++;
- tStat->envdist = tcache->ampEnv.envlevel[tStat->envstat] - tStat->envbase;
-
- if(tStat->envdist != 0) {
- tStat->counter = abs(tStat->envsize / tStat->envdist);
- //LOG_MSG("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
- } else {
- tStat->counter = 0;
- //LOG_MSG("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 * (int32)tcache->amplevel) >> 7;
-
+ file->close();
+ delete file;
}
-
- // Prevlevel storage is bottle neck
- tStat->prevlevel = tc;
-
- //Bias level crap stuff now
- //I unrolled the loop
-
- int dist,bias;
-
- // Bias 1
- if(tcache->ampblevel[0]!=0) {
- bias = tcache->ampbias[0];
- if(tcache->ampdir[0]==0) {
- // < Bias
- if(poly->freqnum < bias) {
- dist = bias-poly->freqnum;
- tc = (tc * ampbiastable[tcache->ampblevel[0]][dist]) >> 8;
+}
- }
- } else {
- // > Bias
- if(poly->freqnum > bias) {
- dist = poly->freqnum-bias;
- tc = (tc * ampbiastable[tcache->ampblevel[0]][dist]) >> 8;
- }
- }
+bool Synth::loadPreset(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read);
+ if (file == NULL) {
+ printDebug("*** Error: Failed to load preset %s", filename);
+ return false;
}
-
- //Bias 2
- if(tcache->ampblevel[1]!=0) {
- bias = tcache->ampbias[1];
- if(tcache->ampdir[1]==0) {
- // < Bias
- if(poly->freqnum < bias) {
- dist = bias-poly->freqnum;
- tc = (tc * ampbiastable[tcache->ampblevel[1]][dist]) >> 8;
-
- }
- } else {
- // > Bias
- if(poly->freqnum > bias) {
- dist = poly->freqnum-bias;
- tc = (tc * ampbiastable[tcache->ampblevel[1]][dist]) >> 8;
+ bool inSys = false;
+ Bit8u sysexBuf[SYSEX_SIZE];
+ Bit16u syslen = 0;
+ int filePos = 0;
+ bool rc = true;
+ for (;;) {
+ int fc = file->readByte();
+ if (fc == -1) {
+ if (!file->isEOF()) {
+ rc = false;
}
+ break;
}
- }
-
-
-
- return tc;
-}
-
-
-INLINE int32 getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly) {
- patchCache *tcache = pStat->tcache;
- dpoly::partialStatus::envstatus *tStat = &pStat->envs[PITCHENV];
-
- int32 tc;
- pStat->pitchsustain = false;
- if(tStat->decaying) {
-
- if((pStat->isDecayed) || (tStat->envpos >= tStat->envsize)) {
- tc = tcache->pitchEnv.level[4];
- } else {
- tc = tStat->envbase;
- tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
- }
- } else {
-
- if(tStat->envstat==3) {
- tc =tcache->pitchsustain;
- if(poly->sustain) {
- pStat->pitchsustain = true;
- } else {
- StartDecay(PITCHENV, tc, pStat, poly);
- }
-
- } else {
-
- if((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
- tStat->envstat++;
-
- tStat->envbase = tcache->pitchEnv.level[tStat->envstat];
- tStat->envsize = (envtimetable[(int)tcache->pitchEnv.time[tStat->envstat]] * timekeytable[(int)tcache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8;
-
-
- tStat->envpos = 0;
- tStat->envsize++;
- tStat->envdist = tcache->pitchEnv.level[tStat->envstat+1] - tStat->envbase;
- }
-
- tc = tStat->envbase;
- tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
-
+ Bit8u c = (Bit8u)fc;
+ sysexBuf[syslen] = c;
+ syslen++;
+ filePos++;
+ if (c==0xf0)
+ inSys = true;
+ if ((c==0xf7) && (inSys)) {
+ playSysex(&sysexBuf[0],syslen);
+ inSys = false;
+ syslen = 0;
}
- tStat->prevlevel = tc;
-
-
}
-
- return tc;
-
+ closeFile(file);
+ return rc;
}
-
-INLINE int32 getFiltEnvelope(int16 wg, dpoly::partialStatus *pStat, dpoly *poly) {
-
- int reshigh;
-
- //float *hist = pStat->history;
- //int64 *hist = pStat->history;
- //long *hist = pStat->history;
- int filt,cutoff,depth,keyfollow, realfollow;
-
- patchCache *tcache = pStat->tcache;
- dpoly::partialStatus::envstatus *tStat = &pStat->envs[FILTENV];
-
- keyfollow = pStat->filtval;
- realfollow = pStat->realval;
-
- int fr = poly->freqnum;
-
- if(tStat->decaying) {
- if(pStat->isDecayed) {
- reshigh = 0;
- } else {
- reshigh = tStat->envbase;
- reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
- if(tStat->envpos >= tStat->envsize) reshigh = 0;
- }
-
- } else {
- if(tStat->envstat==4) {
- reshigh =tcache->filtsustain;
- if(!poly->sustain) StartDecay(FILTENV, reshigh, pStat, poly);
- } else {
-
- if((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
- if(tStat->envstat==-1) {
- tStat->envbase = 0;
- } else {
- tStat->envbase = tcache->filtEnv.envlevel[tStat->envstat];
- }
- tStat->envstat++;
- tStat->envpos = 0;
- if(tStat->envstat==3) {
- tStat->envsize = lasttimetable[(int)tcache->filtEnv.envtime[tStat->envstat]];
- } else {
- tStat->envsize = (envtimetable[(int)tcache->filtEnv.envtime[tStat->envstat]] * timekeytable[(int)tcache->filtEnv.envtkf][poly->freqnum]) >> 8;
- }
-
- tStat->envsize++;
- tStat->envdist = tcache->filtEnv.envlevel[tStat->envstat] - tStat->envbase;
- }
-
- reshigh = tStat->envbase;
- reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
-
- }
- tStat->prevlevel = reshigh;
+unsigned char calcChecksum(unsigned char *data, unsigned int len, unsigned char checksum) {
+ for (unsigned int i = 0; i < len; i++) {
+ checksum = checksum + data[i];
}
-
- cutoff = (tcache->filtEnv.cutoff);
-
- //if(tcache->waveform==1) reshigh = (reshigh * 3) >> 2;
-
- depth = (tcache->filtEnv.envdepth);
-
- //int sensedep = (depth * 127-tcache->filtEnv.envsense) >> 7;
- depth = (depth * filveltable[poly->vel][(int)tcache->filtEnv.envsense]) >> 8;
-
- int bias = tcache->tvfbias;
- int dist;
-
-
- if(bias!=0) {
- //LOG_MSG("Cutoff before %d", cutoff);
- if(tcache->tvfdir == 0) {
- if(fr < bias) {
- dist = bias - fr;
- cutoff = (cutoff * fbiastable[tcache->tvfblevel][dist]) >> 8;
-
- }
- } else {
- // > Bias
- if(fr > bias) {
- dist = fr - bias;
- cutoff = (cutoff * fbiastable[tcache->tvfblevel][dist]) >> 8;
- }
-
- }
- //LOG_MSG("Cutoff after %d", cutoff);
-
- }
-
-
-
- depth = (depth * fildeptable[tcache->tvfdepth][fr]) >> 8;
- reshigh = (reshigh * depth) >> 7;
-
-
- int32 tmp;
-
- cutoff *= keyfollow;
- cutoff /= realfollow;
-
- reshigh *= keyfollow;
- reshigh /= realfollow;
-
- if(cutoff>100) cutoff = 100;
- if(reshigh>100) reshigh = 100;
- if(cutoff<0) cutoff = 0;
- if(reshigh<0) reshigh = 0;
- tmp = nfilttable[fr][cutoff][reshigh];
- //tmp *= keyfollow;
- //tmp /= realfollow;
-
- //LOG_MSG("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256);
-
- return tmp;
- //reshigh = (reshigh * depth * 6)>>10;
-
- filt = (cutoff + reshigh) * keyfollow;
- filt = filt / realfollow;
-
-
-
-
- if(filt<0) filt = 0;
- if(filt>=200) filt = 199;
- tmp = filttable[(int)tcache->waveform][fr][filt];
- return tmp;
-
+ checksum = checksum & 0x7f;
+ if (checksum)
+ checksum = 0x80 - checksum;
+ return checksum;
}
-MidiChannel::MidiChannel(int samp, int cnum) {
- isRy = holdpedal = isPlaying = false;
- volumesetting.rightvol = volumesetting.leftvol = volumesetting.rightvol2 = volumesetting.leftvol2 = 32767;
- patch = storedpatch = 0;
- sampRate = samp;
- channum = cnum;
- volume = 102;
- panpot = 64;
- init = true;
- bend = 0x1000;
- memset(polyTable,0,sizeof(polyTable));
- memset(pcache, 0, sizeof(pcache));
-
- if(cnum==8) {
- isRy = true;
- int pan;
- volume = 102;
- // Cache drum patches
- int q,m;
- for(q=0;q<30;q++) {
- SetPatch(0,q);
- for(m=0;m<42;m++) {
- if(DrumTable[m]==q) {
- pan = drmPanTable[m];
- if(pan<64) {
- drumPan[q][0] = 32767; // lv
- drumPan[q][1] = pan << 9; // rv
- } else {
- drumPan[q][0] = (63-(pan-63)) << 9; // lv
- drumPan[q][1] = 32767; // rv
- }
+bool Synth::loadDrums(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read);
+ if (file == NULL) {
+ return false;
+ }
+ int drumnum = 0;
+ for (;;) {
+ //Read common area
+ TimbreParam *timbre = &mt32ram.params.timbres[drumnum + 192].timbre;
+ if (file->read(&timbre->common, 14) != 14)
+ break;
+ char drumname[11];
+ strncpy(drumname, timbre->common.name, 10);
+ drumname[10] = 0;
+ bool breakout = false;
+ for (int t=0;t<4;t++) {
+ if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
+ if (file->read(&timbre->partial[t], 58) != 58) {
+ breakout = true;
break;
}
+ //printDebug("Loaded drum #%d (%s) - t %d", drumnum, drumname, t);
}
}
+ if (breakout) {
+ break;
+ }
+ //printDebug("Loaded drum #%d (%s)", drumnum, drumname);
+ drumnum++;
}
- init = false;
-}
-
-INLINE int MidiChannel::FixBiaslevel(int srcpnt, int *dir) {
- int noteat = srcpnt & 63;
- int outnote;
- *dir = 1;
- if(srcpnt < 64) *dir = 0;
- outnote = 33 + noteat;
- //LOG_MSG("Bias note %d, dir %d", outnote, *dir);
-
- return outnote;
-
-}
-
-INLINE int MidiChannel::FixKeyfollow(int srckey, int *dir) {
- if (srckey>=0 && srckey<=16) {
- //int keyfix[17] = { 256, 128, 64, 0, 32, 64, 96, 128, 128+32, 192, 192+32, 256, 256+64, 256+128, 512, 259, 269 };
- 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};
-
- if (srckey<3)
- *dir = -1;
- else if (srckey==3)
- *dir = 0;
- else
- *dir = 1;
-
- return keyfix[srckey];
- } else {
- //LOG_MSG("Missed key: %d", srckey);
- return 256;
- }
-}
-
-
-void MidiChannel::RefreshPatch() {
- SetPatch(storedpatch,-1);
+ closeFile(file);
+ return true;
}
-void MidiChannel::SetPatch(int patchnum,int drumNum) {
- int i, j, k, pcm,t;
-
- //int chanoff = channum;
- /* TRISTAN: flush all partials on this channel. This is a hack. */
-// for(i=0; i < MAXPARTIALS; i++)
-// if(partTable[i]->ownerChan == channum)
-// partTable[i]->isActive = false;
-
- /* TRISTAN: check if any partials are still playing on this channel, if *
- * so then duplicate the cached data from the channel to the partial so that *
- * we can change the channels cache without affecting the partial. Hopefully *
- * this is fairly rare. */
- if (storedpatch != patchnum)
- for (i = 0; i < MAXPARTIALS; i++)
- if(partTable[i]->ownerChan == channum)
- if (partTable[i]->isActive)
- {
- /* copy cache data */
- for (j = 0; j < 4; j++)
- partTable[i]->cachebackup[j] = pcache[j];
-
- /* update pointers */
- for (j = 0; j < partTable[i]->timbreNum; j++)
- for (k = 0; k < 4; k++)
- partTable[i]->tmppoly[j].pStatus[k].tcache = partTable[i]->cachebackup + k;
-
- partTable[i]->tcache = partTable[i]->cachebackup;
- }
-
- storedpatch = patchnum;
-
- patch = (mt32ram.params.pSettings[patchnum].timbreGroup * 64) + mt32ram.params.pSettings[patchnum].timbreNum;
-
- timbreParam timSrc;
- //timSrc = mt32ram.params.patch[patch];
- if (drumNum==-1) {
- timSrc = mt32ram.params.timTemp[channum];
- memset(&currentInstr,0,16);
- memcpy(&currentInstr,timSrc.common.name,10);
-
-
- } else {
- // This is to cache all the drum tibres ahead of time
- timSrc = drums[drumNum];
- }
- //LOG_MSG("Reloading patch %d", channum);
- sustain = (timSrc.common.nosustain == 0);
- P1Mix = PartMixStruct[(int)timSrc.common.pstruct12];
- P2Mix = PartMixStruct[(int)timSrc.common.pstruct34];
-
- //sustain = true;
- partialCount = 0;
-
- for(t=0;t<4;t++) {
-
- // Calculate and cache common parameters
- pcm = timSrc.partial[t].wg.pcmwave;
- pcache[t].rawPCM = pcm;
-
- pcache[t].convPCM = PCMList[pcm];
- pcache[t].useBender = (timSrc.partial[t].wg.bender == 1);
-
- // pcm > -1
- switch (t) {
- case 0:
- pcache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct12] >> 1) & 0x1;
+void Synth::dumpDrums(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_write);
+ if (file == NULL)
+ return;
+ char dumbtext[10];
+ memset(dumbtext,0,10);
+ for (int drumnum=0;drumnum<30;drumnum++) {
+ // Sysex header
+ if (!file->writeByte(0xf0))
break;
- case 1:
- pcache[t].PCMPartial = PartialStruct[(int)timSrc.common.pstruct12] & 0x1;
+ if (!file->writeByte(0x41))
break;
- case 2:
- pcache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct34] >> 1) & 0x1;
+ if (!file->writeByte(0x10))
break;
- case 3:
- pcache[t].PCMPartial = PartialStruct[(int)timSrc.common.pstruct34] & 0x1;
+ if (!file->writeByte(0x16))
break;
- default:
+ if (!file->writeByte(0x12))
break;
- }
-
-
- if( ((timSrc.common.pmute >> (t)) & 0x1) == 1 ) {
- pcache[t].playPartial = true;
- pcache[t].usePartial = true;
-
- // Hack and a half... needed so drum partial numbers come through
- pcache[0].partCount = t+1;
-
- partialCount++;
- } else {
- pcache[t].playPartial = false;
- pcache[t].usePartial = false;
- continue;
- }
-
- pcache[t].sustain = (timSrc.common.nosustain == 0);
- pcache[t].waveform = timSrc.partial[t].wg.waveform;
- pcache[t].pulsewidth = timSrc.partial[t].wg.pulsewid;
- pcache[t].pwsens = timSrc.partial[t].wg.pwvelo;
- pcache[t].pitchkeyfollow = FixKeyfollow(timSrc.partial[t].wg.keyfollow, &pcache[t].pitchkeydir);
-
- // Calculate and cache pitch stuff
- pcache[t].pitchshift = (timSrc.partial[t].wg.coarse+mt32ram.params.pSettings[patchnum].keyShift);
- int32 pFine, tFine, fShift;
- pFine = (int32)timSrc.partial[t].wg.fine;
- tFine = (int32)mt32ram.params.pSettings[patchnum].fineTune;
- fShift = ((pFine - 50) + (tFine - 50)) + 100;
- pcache[t].fineshift = finetable[fShift];
-
- keyshift = mt32ram.params.pSettings[patchnum].keyShift-24;
- assignmode = mt32ram.params.pSettings[patchnum].assignMode;
-
- pcache[t].pitchEnv = timSrc.partial[t].env;
- pcache[t].pitchEnv.sensitivity = (int)((float)pcache[t].pitchEnv.sensitivity*1.27);
- pcache[t].pitchsustain = pcache[t].pitchEnv.level[3];
-
- // Calculate and cache TVA envelope stuff
- pcache[t].ampEnv = timSrc.partial[t].tva;
- int l;
- for(l=0;l<4;l++) {
- pcache[t].ampEnv.envlevel[l] = (int)((float)pcache[t].ampEnv.envlevel[l]*1.27);
- }
- pcache[t].ampEnv.level = (int)((float)pcache[t].ampEnv.level*1.27);
- float tvelo = ((float)pcache[t].ampEnv.velosens/100.0);
- float velo = (fabs(tvelo-.5)/.5);
- pcache[t].ampenvdir = 0;
- if(tvelo<.5) pcache[t].ampenvdir = 1;
- velo *= 63.0;
- pcache[t].ampEnv.velosens = (int)(velo);
-
- pcache[t].ampbias[0] = FixBiaslevel(pcache[t].ampEnv.biaspoint1, &pcache[t].ampdir[0]);
- pcache[t].ampblevel[0] = 12-pcache[t].ampEnv.biaslevel1;
- pcache[t].ampbias[1] = FixBiaslevel(pcache[t].ampEnv.biaspoint2, &pcache[t].ampdir[1]);
- pcache[t].ampblevel[1] = 12-pcache[t].ampEnv.biaslevel2;
- pcache[t].ampdepth = pcache[t].ampEnv.envvkf * pcache[t].ampEnv.envvkf;
- pcache[t].ampsustain = pcache[t].ampEnv.envlevel[3];
- pcache[t].amplevel = pcache[t].ampEnv.level;
-
-
- // Calculate and cache filter stuff
- pcache[t].filtEnv = timSrc.partial[t].tvf;
- pcache[t].tvfdepth = pcache[t].filtEnv.envdkf;
- pcache[t].filtkeyfollow = FixKeyfollow(pcache[t].filtEnv.keyfollow, &pcache[t].keydir);
- pcache[t].filtEnv.envdepth = (int)((float)pcache[t].filtEnv.envdepth * 1.27);
- pcache[t].tvfbias = FixBiaslevel(pcache[t].filtEnv.biaspoint, &pcache[t].tvfdir);
- pcache[t].tvfblevel = pcache[t].filtEnv.biaslevel;
- pcache[t].filtsustain = pcache[t].filtEnv.envlevel[3];
-
- // Calculate and cache LFO stuff
- //pcache[t].lfodepth = (int)((float)timSrc.partial[t].lfo.depth * 1.27);
- pcache[t].lfodepth = timSrc.partial[t].lfo.depth;
- pcache[t].lfoperiod = lfotable[(int)timSrc.partial[t].lfo.rate];
- pcache[t].lforate = timSrc.partial[t].lfo.rate;
- pcache[t].modsense = timSrc.partial[t].lfo.modsense;
-
- }
- //LOG_MSG("Res 1: %d 2: %d 3: %d 4: %d", pcache[0].waveform, pcache[1].waveform, pcache[2].waveform, pcache[3].waveform);
-
- if(drumNum!=-1) memcpy(drumCache[drumNum],pcache,sizeof(pcache));
- if(!init) AllStop();
-
- //LOG_MSG("Channel #%d set instrument: %s - %d - %d - %d - %d - pc %d", chanoff, currentInstr, timSrc.partial[0].wg.pcmwave, timSrc.partial[1].wg.pcmwave, timSrc.partial[2].wg.pcmwave, timSrc.partial[3].wg.pcmwave, pcache[0].partCount);
-
-}
-
-void MidiChannel::SetVolume(int vol) {
-
- volume = voltable[vol];
-
-}
-
-void MidiChannel::SetPan(int pan) {
- panpot = pan;
-
- if(pan<64) {
- volumesetting.leftvol = 32767;
- volumesetting.rightvol = pan << 9;
-
- }
- if(pan>=64) {
- volumesetting.rightvol = 32767;
- volumesetting.leftvol = (63-(pan-63)) << 9;
-
- }
-
-
-
- //LOG(LOG_ERROR|LOG_MISC,"Pan %d",panpot);
-}
-
-INLINE int16 RingMod(int16 p1, int16 p2, bool useFirst) {
- if(useFirst) {
- //return (int16)( ( ((float)p1/32767.0) * ((float)p2/32767.0) ) * 32767);
- return (int16)( ((int32)p1 * (int32)p2) >> 15);
-
- } else {
- // An interesting undocumented feature of the MT-32
- // Putting ring mod on a muted partial introduces noise to the ring modulator
- // Dune 2 makes use of this
- return (int16)( ((int32)smallnoise[samplepos/100] * (int32)p2) >> 15);
- }
-}
-
-
-INLINE void MidiChannel::InitKeyFollow(dpoly *tpoly) {
- // Setup partial keyfollow
- int keyfollow = 0;
- int tmpval = tpoly->freqnum,t;
- int keyedval;
-
- // Note follow relative to middle C
- for(t=0;t<4;t++) {
-
- // Calculate keyfollow for pitch
- switch(pcache[t].pitchkeydir) {
- case -1:
- keyfollow = ((int)((MIDDLEC*2-tmpval*2)/2) * pcache[t].pitchkeyfollow) >> 12;
+ int useaddr = drumnum * 256;
+ char lsb = (char)(useaddr & 0x7f);
+ char isb = (char)((useaddr >> 7) & 0x7f);
+ char msb = (char)(((useaddr >> 14) & 0x7f) | 0x08);
+ //Address
+ if (!file->writeByte(msb))
break;
- case 0:
- keyfollow = 0;
+ if (!file->writeByte(isb))
break;
- case 1:
- keyfollow = ((int)((tmpval*2-MIDDLEC*2)/2)*pcache[t].pitchkeyfollow)>>12;
+ if (!file->writeByte(lsb))
break;
- }
- if((pcache[t].pitchkeyfollow>4096) && (pcache[t].pitchkeyfollow<4200)) {
- // Be sure to round up on keys below MIDDLEC
- if(((tmpval*2-MIDDLEC*2)/2) < 0) keyfollow++;
- }
- keyedval = (keyfollow + pcache[t].pitchshift);
- if(keyedval>108) keyedval = 108;
- if(keyedval<12) keyedval = 12;
-
- tpoly->pStatus[t].keyedval = keyedval;
- tpoly->pStatus[t].noteval = tmpval;
- // Calculate keyfollow for filter
-
- int realfol = ((tmpval*2)-(MIDDLEC*2))/2;
- switch(pcache[t].keydir) {
- case -1:
- keyfollow = ((int)((MIDDLEC*2-tmpval*2)/2) * pcache[t].filtkeyfollow )>>12;
+ TimbreParam *timbre = &mt32ram.params.timbres[192 + drumnum].timbre;
+ //Data
+ if (file->write(&timbre->common,0xE) != 0xE)
break;
- case 0:
- keyfollow = tmpval;
+ if (file->write(&timbre->partial[0],0x3A) != 0x3A)
break;
- case 1:
- keyfollow = ((int)((tmpval*2-(MIDDLEC*2))/2) * pcache[t].filtkeyfollow )>>12;
+ if (file->write(&timbre->partial[1],0x3A) != 0x3A)
break;
- }
-
- if (keyfollow>108) keyfollow=108;
- if (keyfollow<-108) keyfollow =-108;
- tpoly->pStatus[t].filtnoval = keyfollow+108;
- tpoly->pStatus[t].filtval = keytable[keyfollow+108];
- tpoly->pStatus[t].realval = keytable[realfol+108];
-
- }
-
-
-}
-
-CPartialMT32 * AllocPartial(int chanNum) {
- int i;
-
- CPartialMT32 *outPart = NULL;
-
-#if MAXPARTIALS == 32
- for(i=0;i<MAXPARTIALS;i++) {
- if(PartialReserveTable[i] == chanNum) {
- if(outPart == NULL) {
- if(!partTable[i]->isActive) {
- outPart = partTable[i];
- }
- }
- }
- }
- if(outPart != NULL) {
- outPart->isActive = true;
- outPart->age = 0;
- } else {
- for(i=0;i<MAXPARTIALS;i++) {
- if(!partTable[i]->isActive) {
- outPart = partTable[i];
- outPart->isActive = true;
- outPart->age = 0;
- break;
- }
- }
- }
-#else
- for(i=0;i<MAXPARTIALS;i++) {
- if(outPart == NULL) {
- if(!partTable[i]->isActive) {
- outPart = partTable[i];
- }
- }
- }
- if(outPart != NULL) {
- outPart->isActive = true;
- outPart->age = 0;
- }
-
-#endif
- return outPart;
-}
-
-int GetFreePartialCount(void) {
- int i;
- int count = 0;
- memset(partialChan,0,sizeof(partialChan));
- for(i=0;i<MAXPARTIALS;i++) {
- if(!partTable[i]->isActive) {
- count++;
- } else {
- partialChan[partTable[i]->ownerChan]++;
- }
- }
- return count;
-}
-
-
-INLINE void MidiChannel::PlayPoly(int m, int f,int vel) {
-
- CPartialMT32 *tmpParts[4];
- f += keyshift;
- if((f<0) || (f >255)) return;
- freq = freqtable[f];
- dpoly *tpoly = &polyTable[m];
-
- tpoly->isPlaying = true;
- tpoly->isDecay = false;
- tpoly->isActive = true;
- tpoly->pcmoff.pcmabs = 0;
- tpoly->freq = freq;
- tpoly->freqnum = f;
- tpoly->pcmdelta = 0x100;
- tpoly->age = 0;
- tpoly->vel = vel;
- tpoly->chan = this->channum;
- tpoly->pedalhold = false;
- tpoly->firstsamp = true;
- memset(tpoly->pStatus,0,sizeof(tpoly->pStatus));
- int x,e;
-
- for(x=0;x<4;x++) {
-
- tpoly->pStatus[x].partNum = x;
- tpoly->pStatus[x].lfopos = 0;
- tpoly->pStatus[x].playPartial = pcache[x].playPartial;
- tpoly->pStatus[x].usePartial = pcache[x].usePartial;
- tpoly->pStatus[x].tcache = &pcache[x];
- tpoly->pStatus[x].pulsewidth = pcache[x].pulsewidth + pwveltable[pcache[x].pwsens][vel];
-
- if(tpoly->pStatus[x].pulsewidth > 100) tpoly->pStatus[x].pulsewidth = 100;
- if(tpoly->pStatus[x].pulsewidth < 0) tpoly->pStatus[x].pulsewidth = 0;
-
- if(pcache[x].playPartial) {
- tmpParts[x] = AllocPartial(channum);
- } else {
- tmpParts[x] = NULL;
- }
- tpoly->pStatus[x].myPart = (void *)tmpParts[x];
-
- for(e=0;e<4;e++) {
- tpoly->pStatus[x].envs[e].envstat = -1;
- tpoly->pStatus[x].envs[e].sustaining = false;
- tpoly->pStatus[x].envs[e].decaying = false;
- tpoly->pStatus[x].envs[e].envpos = 0;
- tpoly->pStatus[x].envs[e].count = 0;
- tpoly->pStatus[x].envs[e].counter = 0;
-
-
- }
- }
-
- bool allnull = true;
- for(x=0;x<4;x++) {
- //if(tmpParts[x] != NULL) allnull = false;
- if(pcache[x].playPartial) allnull = false;
- }
- //if(allnull) LOG_MSG("No paritals to play for %s", this->currentInstr);
-
- tpoly->partCount = pcache[0].partCount;
- tpoly->P1Mix = P1Mix;
- tpoly->P2Mix = P2Mix;
- tpoly->sustain = sustain;
- tpoly->isRy = false;
- tpoly->bendptr = &bend;
- tpoly->volumeptr = &volume;
- tpoly->pansetptr = &volumesetting;
-
- InitKeyFollow(tpoly);
-
- for(x=0;x<4;x++) {
- if(tmpParts[x] != NULL) {
- int pairPart, useMix, partNum;
- switch(x) {
- case 0:
- useMix = P1Mix;
- partNum = 0;
- pairPart = 1;
- break;
- case 1:
- useMix = P1Mix;
- partNum = 1;
- pairPart = 0;
- break;
- case 2:
- useMix = P2Mix;
- partNum = 0;
- pairPart = 3;
- break;
- case 3:
- useMix = P2Mix;
- partNum = 1;
- pairPart = 2;
- break;
- default:
- useMix = P1Mix;
- partNum = 0;
- pairPart = 0;
- break;
- }
- tmpParts[x]->startPartial(tpoly,tpoly->pStatus[x].tcache,&tpoly->pStatus[x],tmpParts[pairPart],useMix,partNum,channum,x);
- tpoly->partActive[x] = true;
- } else {
- tpoly->partActive[x] = false;
- }
- }
-
-#if DISPLAYINSTR == 1
- memset(&currentInstr,0,16);
- memcpy(&currentInstr,mt32ram.params.patch[patch].common.name,10);
- //LOG_MSG("MT32 chan %d (\"%s\") s note poly %d - Vel %d Freq %d Vol %d", channum, currentInstr, m, vel, f, volume);
-#endif
-}
-
-INLINE void MidiChannel::PlayDrum(int m, int f,int vel) {
- if(!((f>=35) && (f<= 76))) return;
- CPartialMT32 *tmpParts[4];
- freq = freqtable[60];
- dpoly *tpoly = &polyTable[m];
-
- tpoly->drumnum = f;
- tpoly->isPlaying = true;
- tpoly->isDecay = false;
- tpoly->isActive = true;
- tpoly->pcmnum = DrumTable[f-35];
- tpoly->pcmoff.pcmabs = 0;
- tpoly->freq = freq;
- tpoly->freqnum = 60;
- tpoly->pcmdelta = 0x100;
- tpoly->age = 0;
- tpoly->vel = vel;
- tpoly->chan = this->channum;
- tpoly->pedalhold = false;
- tpoly->firstsamp = true;
- memset(tpoly->pStatus,0,sizeof(tpoly->pStatus));
- memcpy(pcache,drumCache[tpoly->pcmnum],sizeof(pcache));
- int x,e;
- for(x=0;x<4;x++) {
- tpoly->pStatus[x].partNum = x;
- tpoly->pStatus[x].playPartial = pcache[x].playPartial;
- tpoly->pStatus[x].usePartial = pcache[x].usePartial;
- tpoly->pStatus[x].tcache = &drumCache[tpoly->pcmnum][x];
-
- if(pcache[x].playPartial) {
- tmpParts[x] = AllocPartial(channum);
- } else {
- tmpParts[x] = NULL;
- }
- tpoly->pStatus[x].myPart = (void *)tmpParts[x];
-
- for(e=0;e<4;e++) {
- tpoly->pStatus[x].envs[e].envstat = -1;
- tpoly->pStatus[x].envs[e].count = 0;
- tpoly->pStatus[x].envs[e].counter = 0;
- }
- }
-
- tpoly->P1Mix = PartMixStruct[(int)drums[tpoly->pcmnum].common.pstruct12];
- tpoly->P2Mix = PartMixStruct[(int)drums[tpoly->pcmnum].common.pstruct34];
- tpoly->sustain = (drums[tpoly->pcmnum].common.nosustain == 0);
- tpoly->isRy = true;
- tpoly->bendptr = &tpoly->drumbend;
- tpoly->drumbend = 0x1000;
-
- tpoly->partCount = pcache[0].partCount;
- tpoly->volumeptr = &volume;
- tpoly->pansetptr = &volumesetting;
-
- InitKeyFollow(tpoly);
-
- for(x=0;x<4;x++) {
- if(tmpParts[x] != NULL) {
- int pairPart, useMix, partNum;
- switch(x) {
- case 0:
- useMix = P1Mix;
- partNum = 0;
- pairPart = 1;
- break;
- case 1:
- useMix = P1Mix;
- partNum = 1;
- pairPart = 0;
- break;
- case 2:
- useMix = P2Mix;
- partNum = 0;
- pairPart = 3;
- break;
- case 3:
- useMix = P2Mix;
- partNum = 1;
- pairPart = 2;
- break;
- default:
- useMix = P1Mix;
- partNum = 0;
- pairPart = 0;
- break;
- }
- tmpParts[x]->startPartial(tpoly,tpoly->pStatus[x].tcache,&tpoly->pStatus[x],tmpParts[pairPart],useMix,partNum,channum,x);
- }
- }
-
-#if DISPLAYINSTR == 1
- memset(&currentInstr,0,16);
- memcpy(&currentInstr,drums[tpoly->pcmnum].common.name,10);
- //LOG_MSG("MT32 drum chan (f %d = %d) (\"%s\") starting note poly %d - Velocity %d", f, tpoly->pcmnum, currentInstr, m, vel);
-#endif
-}
-
-/*
-bool FreePartials(int needed, int chanNum) {
- int i;
- int myChanPrior = (int)mt32ram.params.system.reserveSettings[chanNum];
- if(myChanPrior<partialChan[chanNum]) {
- //This can have more channels, must kill off those with less priority
- int most, mostchan;
- while(needed > 0) {
- int selectChan = -1;
- //Find the worst offender with more partials than allocated and kill them
- most = -1;
- mostchan = -1;
- int diff;
-
- for(i=0;i<9;i++) {
- diff = partialChan[i] - (int)mt32ram.params.system.reserveSettings[i];
-
- if(diff>0) {
- if(diff>most) {
- most = diff;
- mostchan = i;
- }
- }
- }
- selectChan = mostchan;
- if(selectChan==-1) {
- // All channels are within the allocated limits, you suck
- // Look for first partial not of this channel that's decaying perhaps?
- return false;
- }
- bool found;
- int oldest;
- int oldnum;
- while(partialChan[selectChan] > (int)mt32ram.params.system.reserveSettings[selectChan]) {
- oldest = -1;
- oldnum = -1;
- found = false;
- for(i=0;i<32;i++) {
- if(partTable[i]->isActive) {
- if(partTable[i]->ownerChan == selectChan) {
- found = true;
- if(partTable[i]->age > oldest) {
- oldest = partTable[i]->age;
- oldnum = i;
- }
- }
- }
- }
- if(!found) break;
- partTable[oldnum]->stopPartial();
- --partialChan[selectChan];
- --needed;
- }
-
- }
- return true;
-
- } else {
- //This channel 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<32;i++) {
- if(partTable[i]->isActive) {
- if(partTable[i]->ownerChan == chanNum) {
- found = true;
- if(partTable[i]->age > oldest) {
- oldest = partTable[i]->age;
- oldnum = i;
- }
- }
- }
- }
- if(!found) break;
- partTable[oldnum]->stopPartial();
- --needed;
- }
- // Couldn't free enough partials, sorry
- if(needed>0) return false;
- return true;
- }
-
-}
-*/
-bool FreePartials(int needed, int chanNum) {
-
- int i;
-#if MAXPARTIALS == 32
- // Reclaim partials reserved for this channel
- // Kill those that are already decaying first
- /*
- for(i=0;i<32;i++) {
- if(PartialReserveTable[i] == chanNum) {
- if(partTable[i]->ownerChan != chanNum) {
- if(partTable[i]->partCache->envs[AMPENV].decaying) {
- partTable[i]->isActive = false;
- --needed;
- if(needed<=0) return true;
- }
- }
- }
-
- }*/
- // Then kill those with the lowest channel priority --- oldest at the moment
- bool found;
- int64 prior, priornum;
- dpoly *killPoly;
- found = true;
- while(found) {
- found = false;
- prior = -1;
- priornum = -1;
-
- for(i=0;i<32;i++) {
- if(PartialReserveTable[i] == chanNum) {
- if(partTable[i]->isActive) {
- if(partTable[i]->ownerChan != chanNum) {
- /*
- if(mt32ram.params.system.reserveSettings[partTable[i]->ownerChan] < prior) {
- prior = mt32ram.params.system.reserveSettings[partTable[i]->ownerChan];
- priornum = i;
- }*/
- if(partTable[i]->age > prior) {
- prior = partTable[i]->age;
- priornum = i;
- }
-
- found = true;
- }
- }
- }
- }
- if(priornum != -1) {
- partTable[priornum]->isActive = false;
-
- killPoly = partTable[priornum]->tmppoly;
- killPoly->partActive[partTable[priornum]->timbreNum] = false;
- killPoly->isActive = killPoly->partActive[0] || killPoly->partActive[1] || killPoly->partActive[2] || killPoly->partActive[3];
- --needed;
- if(needed<=0) return true;
- }
- }
-
-
- // Kill off the oldest partials within this channel
- int64 oldest, oldlist;
-
- while(needed>0) {
- oldest = -1;
- oldlist = -1;
- for(i=0;i<32;i++) {
- if(partTable[i]->isActive) {
- if(partTable[i]->ownerChan == chanNum) {
- if(partTable[i]->age > oldest) {
- oldest = partTable[i]->age;
- oldlist = i;
- }
- }
- }
- }
- if(oldlist != -1) {
- partTable[oldlist]->isActive = false;
- killPoly = partTable[oldlist]->tmppoly;
- killPoly->partActive[partTable[oldlist]->timbreNum] = false;
- killPoly->isActive = killPoly->partActive[0] || killPoly->partActive[1] || killPoly->partActive[2] || killPoly->partActive[3];
- --needed;
- } else {
+ if (file->write(&timbre->partial[2],0x3A) != 0x3A)
break;
- }
- }
- if(needed<=0) return true;
-
-
- return false;
-#else
- //No priority table when not using standard MT-32 configuration
- // Kill off the oldest partials within this channel
- int oldest, oldlist;
- dpoly *killPoly;
- oldest = -1;
- oldlist = -1;
- while(needed>0) {
- for(i=0;i<MAXPARTIALS;i++) {
- if(partTable[i]->ownerChan == chanNum) {
- if(partTable[i]->age > oldest) {
- oldest = partTable[i]->age;
- oldlist = i;
- }
- }
- }
- if(oldlist != -1) {
- partTable[oldlist]->isActive = false;
- killPoly = partTable[oldlist]->tmppoly;
- killPoly->partActive[partTable[oldlist]->timbreNum] = false;
- killPoly->isActive = killPoly->partActive[0] || killPoly->partActive[1] || killPoly->partActive[2] || killPoly->partActive[3];
- --needed;
- } else {
+ if (file->write(&timbre->partial[3],0x3A) != 0x3A)
break;
- }
- }
- if(needed<=0) return true;
- //LOG_MSG("Out of paritals!");
- return false;
-#endif
-
-
-}
-
-
-void MidiChannel::CheckNoteList() {
- int q,t;
- for(q=0;q<MAXPOLY;q++) {
- if(polyTable[q].isActive) {
- bool isActive = false;
- CPartialMT32 * tmpPart;
- for(t=0;t<4;t++) {
- tmpPart = (CPartialMT32 *)polyTable[q].pStatus[t].myPart;
- if(tmpPart != NULL) {
- if(tmpPart->ownerChan == channum) {
- isActive = isActive || tmpPart->isActive;
- }
- }
- }
- polyTable[q].isActive = isActive;
-
- }
- }
-}
-
-INLINE void MidiChannel::PlayNote(int f,int vel) {
- int m;
- sndoff=0;
- velocity = vel;
-
- isPlaying = false;
-
- //if(channum!=0) return;
-
- if(isRy) memcpy(pcache,drumCache[DrumTable[f-35]],sizeof(pcache));
-
- // POLY1 mode, Single Assign
- // Haven't found any software that uses any of the other poly modes
- if(!isRy) {
- for(m=0;m<MAXPOLY;m++) {
- if((polyTable[m].isActive) && (polyTable[m].freqnum == f)) {
- StopNote(f,vel);
- break;
- }
- }
- }
-
- int needPartials = pcache[0].partCount;
-
- if(needPartials > GetFreePartialCount()) {
- if(!FreePartials(needPartials, channum)) {
- // Before we quit, see if there are other channels willing to donate
- if(needPartials > GetFreePartialCount()) {
- // Unable to get needed partials to play this note
- return;
- }
- }
- }
-
- // Find free note allocator
- for(m=0;m<MAXPOLY;m++) {
- if(!polyTable[m].isActive){
- isPlaying=true;
- if (!isRy) {
- PlayPoly(m,f,vel);
- } else {
- if(DrumTable[f-35]>-1) PlayDrum(m,f,vel);
- }
+ //Checksum
+ unsigned char *dat = (unsigned char *)timbre;
+ unsigned char checksum = calcChecksum(dat, 246, msb + isb + lsb);
+ if (!file->writeByte(checksum))
break;
- }
- }
-
-
-}
-void MidiChannel::AllStop() {
- int q,t;
- for(q=0;q<MAXPOLY;q++) {
- dpoly *tpoly = &polyTable[q];
- if(tpoly->isPlaying) {
- tpoly->isDecay = true;
- for(t=0;t<4;t++) {
-
- //memset(tpoly->pStatus[t].decay,0,sizeof(tpoly->pStatus[t].decay));
- //tpoly->pStatus[t].isDecayed = 0;
- //memset(tpoly->pStatus[t].decaying,true,sizeof(tpoly->pStatus[t].decaying));
-
- StartDecay(AMPENV,tpoly->pStatus[t].envs[AMPENV].prevlevel, &tpoly->pStatus[t], tpoly);
- StartDecay(FILTENV,tpoly->pStatus[t].envs[FILTENV].prevlevel, &tpoly->pStatus[t], tpoly);
- StartDecay(PITCHENV,tpoly->pStatus[t].envs[PITCHENV].prevlevel, &tpoly->pStatus[t], tpoly);
-
- tpoly->pStatus[t].pitchsustain = false;
- }
- tpoly->isPlaying = false;
- }
-
- }
-
-}
-
-void MidiChannel::StopPedalHold() {
- int q;
- for(q=0;q<MAXPOLY;q++) {
- dpoly *tpoly;
- tpoly = &polyTable[q];
- if (tpoly->pedalhold) StopNote(tpoly->freqnum,0);
- }
-
-}
-
-void MidiChannel::StopNote(int f,int vel) {
- // Find oldest note... yes, the MT-32 can be reconfigured to kill different note first
- // This is simplest
- int oldest = -1;
- int oldage = 0;
- int count = 0;
- int q,t;
- bool found = false;
- dpoly *tpoly;
-
- // Non-sustaining instruments ignore stop note commands.
- // They die away eventually anyway
- //if(!tpoly->sustain) return;
-
- //LOG_MSG("MT32 chan %d (\"%s\") stopping note %d", this->channum, currentInstr, f);
-
- for(q=0;q<MAXPOLY;q++) {
- tpoly = &polyTable[q];
-
- if(tpoly->isPlaying) {
- if(tpoly->freqnum == f) {
- if (holdpedal) {
- tpoly->pedalhold = true;
-
- } else {
- if(tpoly->sustain) {
- tpoly->isDecay = true;
-
-
- for(t=0;t<4;t++) {
- //memset(tpoly->pStatus[t].decay,0,sizeof(tpoly->pStatus[t].decay));
- //tpoly->pStatus[t].isDecayed = 0;
- //memset(tpoly->pStatus[t].decaying,true,sizeof(tpoly->pStatus[t].decaying));
- StartDecay(AMPENV,tpoly->pStatus[t].envs[AMPENV].prevlevel, &tpoly->pStatus[t], tpoly);
- StartDecay(FILTENV,tpoly->pStatus[t].envs[FILTENV].prevlevel, &tpoly->pStatus[t], tpoly);
- StartDecay(PITCHENV,tpoly->pStatus[t].envs[PITCHENV].prevlevel, &tpoly->pStatus[t], tpoly);
- tpoly->pStatus[t].pitchsustain = false;
- }
- tpoly->isPlaying = false;
- }
- //return;
- }
- found = true;
-
-
- }
- }
-
- }
-
- if(f!=-1) return;
- oldest = -1;
- for(q=0;q<MAXPOLY;q++) {
- tpoly = &polyTable[q];
-
- if((tpoly->isPlaying) && (!tpoly->isDecay) && (tpoly->chan==channum)) {
- if(tpoly->age>=oldage) {
- oldage = tpoly->age;
- oldest = q;
- }
- count++;
- }
- }
-
- if(oldest!=-1) {
- tpoly = &polyTable[oldest];
- tpoly->isDecay = true;
- for(t=0;t<4;t++) {
- //memset(tpoly->pStatus[t].decay,0,sizeof(tpoly->pStatus[t].decay));
- //tpoly->pStatus[t].isDecayed = 0;
- //memset(tpoly->pStatus[t].decaying,true,sizeof(tpoly->pStatus[t].decaying));
- StartDecay(AMPENV,tpoly->pStatus[t].envs[AMPENV].prevlevel, &tpoly->pStatus[t], tpoly);
- StartDecay(FILTENV,tpoly->pStatus[t].envs[FILTENV].prevlevel, &tpoly->pStatus[t], tpoly);
- StartDecay(PITCHENV,tpoly->pStatus[t].envs[PITCHENV].prevlevel, &tpoly->pStatus[t], tpoly);
-
- tpoly->pStatus[t].pitchsustain = false;
- }
- tpoly->isPlaying = false;
- //LOG(LOG_MISC|LOG_ERROR,"MT32 chan %d stopping note %d, %d remaining", this->channum, oldest, count-1);
+ //End of sysex
+ if (!file->writeByte(0xf7))
+ break;
}
-
+ closeFile(file);
}
-MidiChannel *mchan[16];
-
-#endif
-
-
-bool CSynthMT32::InitTables() {
+bool Synth::loadPCMToROMMap(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read); // Original specified text mode
-#ifdef NOMANSLAND
-
- int noteat,f;
-
- //LOG_MSG("MT-32 Initializing Pitch Tables");
- for(f=-108;f<109;f++) {
- keytable[f + 108] = (int)(256 * pow((float)2, (float)f / (float)24.0));
- //LOG_MSG("KT %d = %d", f, keytable[f+108]);
-
- }
- float ff = 0;
- for(f=0;f<=101;f++) {
- ff = (float)f/100.00;
- sqrtable[f] = (int)(100*sqrt(ff));
- float crapff = ff * (22000.0/32000.0);
- if (crapff>1.0) crapff = 1.0;
- //filttable[f] = (ff) * (22000.0/16000.0);
-
- }
-
- for(f=0;f<8;f++) {
- ff = (float)f/14.00;
- revtable[f] = (ff);
- }
- File fp;
-#if MAKEWAVES == 1
- fp.open("waveforms.raw", File::kFileWriteMode);
-#else
- fp.open("waveforms.raw");
-#endif
- if(!fp.isOpen()) {
- error("Unable to open waveforms.raw");
+ if (file == NULL) {
return false;
}
- for(f=12;f<109;f++) {
-
- //int octave = (f / 12) - 6;
- //int note = f % 12;
-
- //int cents = (octave * 100) + (note * 100);
- //int freq = (int)((double)262 * pow((double)2,(double)((double)cents/1200)));
- int freq = (int)(TUNING * pow( 2.0, ((double)f - 69.0) / 12.0 ));
- freqtable[f] = freq;
- divtable[f] = (int)( ((float)SETRATE / (float)(freq)));
- smalldivtable[f] = divtable[f] << 8;
- divtable[f] = divtable[f] << 16;
- int rsaw,dep;
-
- for(rsaw=0;rsaw<=100;rsaw++) {
- //(66-(((A8-50)/50)^.63)*50)/132
- float fsaw = (float)rsaw;
- if(rsaw<50) fsaw = 50.0;
- int tmpdiv = divtable[f] << 1;
-
- float sawfact = (66.0-(pow((fsaw-50.0)/50,.63)*50.0))/132.0;
- sawtable[f][rsaw] = (int)(sawfact * (float)tmpdiv) >> 16;
- //LOG_MSG("F %d divtable %d saw %d sawtable %d", f, divtable[f]>>16, rsaw, sawtable[f][rsaw]);
-
- }
-
-
- for(dep=0;dep<5;dep++) {
- if(dep>0) {
- float depfac = 3000;
- float ff1, tempdep;
- depfac = (float)depexp[dep];
-
- ff1 = ((float)f - (float)MIDDLEC) / depfac;
- tempdep = pow((float)2,(float)ff) * 256;
- fildeptable[dep][f] = (int)tempdep;
-
- ff1 = exp(tkcatconst[dep] * ((float)MIDDLEC-(float)f)) * tkcatmult[dep];
- timekeytable[dep][f] = (int)(ff1 * 256);
-
- } else {
- fildeptable[dep][f] = 256;
- timekeytable[dep][f] = 256;
- }
- }
- //LOG_MSG("F %d d1 %x d2 %x d3 %x d4 %x d5 %x", f, fildeptable[0][f],fildeptable[1][f],fildeptable[2][f],fildeptable[3][f],fildeptable[4][f]);
-
-
-
-
- noteat = 69-12;
-
-#if MAKEWAVES ==1
- double ampsize = WGAMP;
- int halfdiv = divtable[f] >> 1;
- int fa=0;
-
- float period = ((float)SETRATE / ((float)freq));
- float m=2*(int)(period/2)+1.0f;
- float k=(int)(((float)50.0/100.0)*period);
- double sd = (2.0*PI)/((((float)divtable[f]/65536.0)) * 4.0);
- double sa = 0.0;
-
- //LOG_MSG("F %d sd %f div %d", f, sd, divtable[f]);
-
- int j;
- float dumbfire;
- double square=0.0f;
- double saw = 0.0f;
-
- memset(waveformsize, 0,sizeof(tmpforms));
-
- while(sa<=(2.0*PI)) {
- float sqp;
-
- if(sa<PI) {
- sqp = -1;
- sqp = sqp + (.25 * (sa/PI));
- } else {
- sqp=1;
- sqp = sqp - (.25 * ((sa-PI)/PI));
- }
-
- square=0;
- saw = 0;
- bool odd = true;
- for(int32 sinus=1;(sinus*freq)<(SETRATE);sinus++) {
- float sinusval = (((1.0/((float)sinus))*(sin(((float)sinus)*sa))));
- saw=saw + sinusval;
- }
-
-
- dumbfire = sa/2;
-
- //This works pretty good
- tmpforms[2][fa] += cos(dumbfire) * -ampsize;
- tmpforms[3][(fa*2)] += cos(sa-PI) * -ampsize;
- tmpforms[3][(fa*2)+1] += cos((sa+(sd/2))-PI) * -ampsize;
-
- tmpforms[0][fa] += (saw * -ampsize)/2;
- tmpforms[1][fa] += (saw * ampsize)/2;
-
- //tmpforms[1][fa>>4] += saw * ampsize;
-
-
- //waveforms[1][f][fa] = ((prevv2 - prevv)) * ampsize;
-
-
- fa++;
- sa+=sd;
- }
- //LOG_MSG("f num %d freq %d and fa %d", f, freq, fa);
-
-
- waveformsize[0][f] = waveformsize[1][f] = waveformsize[2][f] = fa*2;
- waveformsize[3][f] = fa*4;
-
- for (int i = 0; i < 4; ++i) {
- waveforms[i][f] = (int16 *)malloc(waveformsize[i][f]);
- memcpy(waveforms[i][f], &tmpforms[i][0],waveformsize[i][f]);
- // TODO / FIXME: The following code is not endian safe!
- out = fp.write(waveforms[i][f],waveformsize[i][f]);
- }
-#else
- waveformsize[0][f] = waveformsize[1][f] = waveformsize[2][f] = divtable[f]>>13;
- waveformsize[3][f] = divtable[f]>>12;
-
- for (int i = 0; i < 4; ++i) {
- waveforms[i][f] = (int16 *)malloc(waveformsize[i][f]);
- for (uint j = 0; j < waveformsize[i][f]/2; ++j)
- waveforms[i][f][j] = fp.readSint16LE();
- }
-#endif
-
-
- // Create the pitch tables
-
- float tuner = (32000.0 / (float)SETRATE) * 65536.0;
-
- wavtable[f] = (int)(tuner * ((float)freq/(float)SAMPLETUNING)); // C4 Tuning?;
- drumdelta[f] = (int)(tuner * ((float)freq/(float)SAMPLETUNING)); // C4 Tuning?;
- int pc,lp,tr,ln;
- for(pc=0;pc<54;pc++) {
- wavtabler[pc][f] = (int)(tuner * ((float)freq/PCM[pc].tune));
- }
- for(lp=0;lp<16;lp++) {
- for(ln=0;ln<16;ln++) {
- looptabler[lp][ln][f] = (int)((float)LoopPatternTuning[lp][ln] * ((float)freq/(float)220.0));
- }
- }
-
- for(tr=0;tr<=200;tr++) {
- float brsq, brsa;
- float ftr = (float)tr;
-
- // Verified exact on MT-32
- if(tr>100) ftr=100+(pow(((ftr-100)/100),3)*100);
- brsq = exp(0.0464 * ftr) / 5;
- //EXP((90*0.0175)^2)
- brsq = exp(pow(((float)tr * 0.0153),2));
-
- // I think this is the one
- brsq = pow((float)10,((float)(tr/50.0)-1));
-
- //brsa = exp(0.04 * ftr) / 5;
-
- //Last good one that worked
- //brsa = exp(pow(((float)tr * 0.0133),2));
- brsa = exp(pow(((float)tr * 0.005),3)) * 125;
-
- //brsa = pow((float)10,((float)(tr/46.5)-1))/2;
- brsa = pow((float)10,((float)(tr/55)-1))/2;
-
- //brsa = pow(exp((ftr-40)/40),1.15);
- //brsq = exp((ftr-35)/35);
- //brsq = exp((ftr-35)/23);
- //brsa = exp((ftr-35)/23);
- //brsq = pow((ftr / 200), LN) * 48.0;
- //brsa = pow((ftr / 200), LN) * 32.0;
- //brsq = brsa =
-
- filttable[0][f][tr] = (int)(((float)freq * brsq)/(float)(SETRATE/2)*FILTERGRAN);
- if(filttable[0][f][tr]>=((FILTERGRAN*15)/16)) filttable[0][f][tr] = ((FILTERGRAN*15)/16);
- filttable[1][f][tr] = (int)(((float)freq * brsa)/(float)(SETRATE/2)*FILTERGRAN);
- if(filttable[1][f][tr]>=((FILTERGRAN*15)/16)) filttable[1][f][tr] = ((FILTERGRAN*15)/16);
-
- }
-
- int cf, tf;
- for(cf=0;cf<=100;cf++) {
- float cfmult = (float)cf;
-
- for(tf=0;tf<=100;tf++) {
-
- //float tfadd = exp((((float)tf / 100.0) - 1.03) * 3.0) * 100;
- //float tfadd = (pow(((float)tf /100.0),4) * 100.0) - 10;
- float tfadd = tf - 0;
-
- if (tfadd < 0) tfadd = 0;
- float freqsum = exp((cfmult + tfadd) / 30.0) / 4.0;
- //float freqsum = exp((cfmult + tfadd) / 33.7) / 3.0;
-
-
- nfilttable[f][cf][tf] = (int)(((float)freq * freqsum)/(float)(SETRATE/2)*FILTERGRAN);
- if(nfilttable[f][cf][tf]>=((FILTERGRAN*15)/16)) nfilttable[f][cf][tf] = ((FILTERGRAN*15)/16);
- }
- }
- }
-
- fp.close();
-
- int j,res;
- float fres = 0, tres = 0;
- for(res=0;res<31;res++) {
- fres = (float)res/30.0;
- ResonFactor[res] = (pow((float)2,log(pow((float)fres,(float)16))) * 2.5)+1.0;
- ResonInv[res] = 1 / ResonFactor[res];
-
- }
-
- for(j=0;j<FILTERGRAN;j++) {
- for(res=0;res<31;res++) {
- tres = ResonFactor[res];
- InitFilter((float)SETRATE, (((float)(j+1.0)/FILTERGRAN)) * ((float)SETRATE/2), filtcoeff[j][res],tres, fres);
-
- // 64-bit variant
-#if FILTER_64BIT == 1
- for(int co=0;co<9;co++) {
- filtcoefffix[j][res][co] = (int64)(filtcoeff[j][res][co] * pow(2,20));
-
- }
-#endif
-
-#if FILTER_INT == 1
- for(int co=0;co<9;co++) {
- filtcoefffix[j][res][co] = (long)(filtcoeff[j][res][co] * pow(2,10));
-
- }
-#endif
- }
-
- }
-
- int period = 65536;
-
- int ang;
- for(ang=0;ang<period;ang++) {
- int halfang = (period / 2);
- int angval = ang % halfang;
- float tval = (((float)angval / (float)halfang) - 0.5) * 2;
- if(ang>=halfang) tval = -tval;
- sintable[ang] = (int)(tval * 50.0)+50;
-
- }
-
-
- //for(ang=0;ang<period;ang++) sintable[period] *= 50;
- int velt, dep;
- float tempdep;
- for(velt=0;velt<128;velt++) {
- for(dep=0;dep<5;dep++) {
- if(dep>0) {
- float ff1 = exp(3.5*tvcatconst[dep] * (59.0-(float)velt)) * tvcatmult[dep];
- tempdep = 256.0 * ff1;
- veltkeytable[dep][velt] = (int)tempdep;
- if((velt % 16) == 0) {
- //LOG_MSG("Key %d, depth %d, factor %d", velt, dep, (int)tempdep);
- }
- } else {
- veltkeytable[dep][velt] = 256;
- }
- }
-#define divpart 14.285714285714285714285714285714
-
- for(dep=-7;dep<8;dep++) {
- float fldep = fabs((float)dep) / 7.0;
- fldep = pow((float)fldep,(float)2.5);
- if(dep<0) fldep = fldep * -1.0;
- pwveltable[dep+7][velt] = int32((fldep * (float)velt * 100) / 128.0);
-
- }
- }
-
- for(dep=0;dep<=100;dep++) {
- for(velt=0;velt<128;velt++) {
- float fdep = (float)dep * 0.000347013; // Another MT-32 constant
- float fv = ((float)velt - 64.0)/7.26;
- float flogdep = pow((float)10, (float)(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 - (pow(((float)dep / 100.0),.25) * ((float)(64-velt) / 96.0));
- filveltable[velt][dep] = (int)(fbase * 256.0);
-
- }
-
- //LOG_MSG("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]);
- }
- }
-
- int lf;
- for(lf=0;lf<=100;lf++) {
- float elf = (float)lf;
-
- // General envelope
- float logtime = elf * 0.088362939;
- envtimetable[lf] = (int)((exp(logtime)/312.12) * (float)SETRATE);
-
- // 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)SETRATE);
-
- //lasttimetable[lf] = (int)((exp(logtime)/(312.12*6)) * (float)SETRATE);
-
- // Finetuning table
- //finetable[lf] = (int) ((pow(2, (((float)lf/100.0)-0.5)/6.0))*4096.0);
-
- }
- for(lf=0;lf<=200;lf++) {
- finetable[lf] = (int) ((pow((float)2, (float)((((float)lf/200.0)-1.0)/12.0))*4096.0));
-
- }
- for(lf=0;lf<=48;lf++) {
- bendtable[lf] = (int) ((pow((float)2, (float)((((float)lf/12.0)-2.0)))*4096.0));
-
- }
-
- float lff;
- for(lf=0;lf<128;lf++) {
- for(velt = 0;velt<64;velt++) {
- lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96));
- //lff = ((128.0 - (float)lf) / 128.0) * ((float)velt / 64);
- //lff = 1.0 / pow(10, lff/2);
-
-
-
- ampveltable[lf][velt] =(int)(lff * 256.0);
- //LOG_MSG("Ampveltable: %d, %d = %d", lf, velt, ampveltable[lf][velt]);
- }
- }
- for(lf=0;lf<=127;lf++) {
- restable[lf] = (int)( (pow((float)2,pow((float)(lf/127.0),(float)2.5))-1)*100 );
- //LOG_MSG("lf %d = amp %d", lf, restable[lf]);
- }
-
- for(lf=0;lf<=127;lf++) {
- //amptable[lf] = (int)( (pow(2,pow((float)lf/127.0,1))-1)*127 );
- //amptable[lf] = lf;
- //float pubical = (float)lf/127.0; // Yes, as in boobical pubical (because its cubical!)
-
- // Converting MIDI to volume.
- // Thanks Microsoft DDK
- // value = 2^(log10((index/127)^4))*127 where index = 0..127
- amptable[lf] = voltable[lf] = (int)(127.0 * pow((float)2,log(pow((float)(lf/127.0),(float)4))));
-
- // Nope, lets try this again
- //amptable[lf] = voltable[lf] = (int)(127.0 * log10((float)lf/12.70));
-
- //amptable[lf] = (int)(pubical * pubical * pubical * 127.0);
-
- // Once more...
-
- //float indec = 128.0 - (float)lf;
- //indec = -(indec / 1.33333);
- voltable[lf] = amptable[lf] = (int)(127.0 * pow((float)lf/127.0,LN));
-
- //indec = 40 * log((float)lf / 127.0);
- //voltable[lf] = (int)(127.0 * exp(indec/40.0));
-
- //LOG_MSG("lf %d = vol %d", lf, voltable[lf]);
- }
- for(lf=0;lf<441;lf++) {
- int myRand;
- myRand = rand();
- myRand = ((myRand - 16383) * WGAMP) >> 18;
- smallnoise[lf] = (int16)myRand;
- }
-
- for(lf=0;lf<=100;lf++) {
- ptable[lf] = (int)(pow((float)2,(float)((float)(lf-50)/25.0)) * 256);
-
- }
- float tdist;
- 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-(.333333f);
- else if (lf==5)
- padjtable[lf] = 4-(.333333f*2);
- else if (lf==6)
- padjtable[lf] = 3;
- else if ((lf>6) && (lf<=12)) {
- tdist = (lf-6.0) / 6.0;
- padjtable[lf] = 3.0 - tdist;
- } else if ((lf>12) && (lf<=25)) {
- tdist = (lf-12.0) / 13.0;
- padjtable[lf] = 2.0 - tdist;
-
- } else {
- tdist = (lf-25.0) / 25.0;
- padjtable[lf] = 1.0 - tdist;
- }
- //LOG_MSG("lf %d = padj %f", lf, padjtable[lf]);
- }
- for(lf=0;lf<=100;lf++) {
- float mv = (float)lf / 100.0;
- float pt = mv-0.5;
- if(pt<0) pt = 0;
-
- pulsetable[lf] = (int)((pt) * 215.04) + 128;
- pulseoffset[lf] = (int)(pt * WGAMP);
-
- /*
- // I am certain of this: Verified by hand LFO log */
- lfotable[lf] = (int32)(((float)SETRATE) / (pow((float)1.088883372,(float)lf) * 0.021236044));
-
- //LOG_MSG("lf %d = lfo %d pulsetable %d", lf, lfotable[lf], pulsetable[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 = exp(0.713619942 * tlf) / 407.4945111;
-
- if(depat<50)
- finalval = 4096.0 * pow((float)2,(float)-lfp);
- else
- finalval = 4096.0 * pow((float)2, (float)lfp);
- pval = (int)(finalval);
-
- penvtable[lf][depat] = pval;
- } else {
- penvtable[lf][depat] = 4096;
-
- }
-
- //LOG_MSG("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp);
-
- }
- }
- for(lf=0;lf<=100;lf++) {
- // Maybe its linear
- // It is - verified on MT-32 - one of the few things linear
- lfp = ((float)lf * .1904) / 310.55;
-
- for(depat=0;depat<=100;depat++) {
- depf = ((float)depat - 50.0) / 50.0;
- //finalval = pow(2, lfp * depf * .5);
- finalval = 4096.0 + (4096.0 * lfp * depf);
-
- pval = (int)(finalval);
-
- lfoptable[lf][depat] = pval;
-
- //LOG_MSG("lf %d depat %d pval %x", lf,depat,pval);
-
- }
- }
-
-
- int distval;
- float amplog, dval;
-
- for(lf=0;lf<=12;lf++) {
- for(distval=0;distval<128;distval++) {
- if(lf==0) {
- amplog = 0;
- dval = 1;
- ampbiastable[lf][distval] = 256;
- } else {
- amplog = pow((float)1.431817011,(float)lf) / PI;
- dval = ((128.0-(float)distval)/128.0);
- amplog = exp(amplog);
- dval = pow(amplog,dval)/amplog;
- ampbiastable[lf][distval] = (int)(dval * 256.0);
- }
- //LOG_MSG("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, ampbiastable[lf][distval],amplog);
- }
-
-
-
- }
-
- for(lf=0;lf<=14;lf++) {
- for(distval=0;distval<128;distval++) {
- float filval = fabs((((float)lf - 7.0) * 12.0) / 7.0);
-
- if(lf==7) {
- amplog = 0;
- dval = 1;
- fbiastable[lf][distval] = 256;
- } else {
- //amplog = pow(1.431817011,filval) / PI;
- amplog = pow((float)1.531817011, (float)filval) / PI;
- dval = ((128.0-(float)distval)/128.0);
- amplog = exp(amplog);
- dval = pow(amplog,dval)/amplog;
- if(lf<8) {
- fbiastable[lf][distval] = (int)(dval * 256.0);
- } else {
- dval = pow((float)dval, (float).3333333);
- if(dval<.01) dval = .01f;
- dval = 1 / dval;
- fbiastable[lf][distval] = (int)(dval * 256.0);
- }
- }
- //LOG_MSG("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, fbiastable[lf][distval],amplog);
- }
-
-
-
- }
-
- // Benchmark 3DNow, Floating point, and SSE filters
-/*
- uint32 bench;
- __time64_t start, end;
- float histval[50];
-
- _time64(&start);
- for(bench=0;bench<20000000;bench++) {
- iir_filter_sse(0,&histval[0],filtcoeff[0][0],0);
- }
- _time64(&end);
- //LOG_MSG("Bench completed in %ld seconds", end - start);
-*/
-
-
-#endif
-
- return true;
-}
-
-bool RecalcWaveforms(int sampRate, recalcStatusCallback callBack) {
-
-#ifdef NOMANSLAND
-
- File fp;
- fp.open("waveforms.raw", File::kFileWriteMode);
-
- if(!fp.isOpen()) return false;
-
- double ampsize = WGAMP;
- int f;
- for(f=12;f<109;f++) {
-
- if(callBack != NULL) {
- int perc = ((f - 12) * 100) / 96;
- (callBack)(perc);
- }
-
- //int octave = (f / 12) - 6;
- //int note = f % 12;
-
- //int cents = (octave * 100) + (note * 100);
- int freq = (int)(TUNING * pow( 2.0, ((double)f - 69.0) / 12.0 ));
- freqtable[f] = freq;
- divtable[f] = (int)( ((float)sampRate / (float)freq) );
- smalldivtable[f] = divtable[f] << 8;
- divtable[f] = divtable[f] << 16;
-
- //int halfdiv = divtable[f] >> 1;
-
- //float period = ((float)sampRate / ((float)freq));
- //float m=2*(int)(period/2)+1.0f;
- //float k=(int)(((float)50.0/100.0)*period);
-
-
- double sd = (2.0*PI)/((float)divtable[f]/4096.0);
- double sa = 0.0;
-
-
- int fa=0;
-
-
- int j;
- float dumbfire;
- double square=0.0f;
- double saw = 0.0f;
-
- memset(tmpforms, 0,sizeof(tmpforms));
-
- while(sa<=(2.0*PI)) {
- float sqp;
-
- if(sa<PI) {
- sqp = -1;
- sqp = sqp + (.25 * (sa/PI));
- } else {
- sqp=1;
- sqp = sqp - (.25 * ((sa-PI)/PI));
- }
-
- square=0;
- saw = 0;
- //bool odd = true;
- for(double sinus=1.0;sinus<256.0;sinus++) {
- float sinusval = (((1/(sinus))*(sin(sinus*sa))));
- if((sinus*freq)<(sampRate*2)) saw=saw + sinusval;
- }
-
- dumbfire = sa /2 ;
-
- //This works pretty good
- tmpforms[2][fa>>4] += (int16)(cos(dumbfire) * -ampsize);
- tmpforms[3][fa>>3] += (int16)(cos(sa-PI) * -ampsize);
-
- tmpforms[0][fa>>4] += (int16)(saw * -ampsize);
- tmpforms[1][fa>>4] += (int16)(saw * ampsize);
-
- fa++;
- sa+=sd;
- }
-
- for(j=0;j<=(divtable[f]>>16);j++) {
- finalforms[0][j] = tmpforms[0][j] >> 5;
- finalforms[1][j] = tmpforms[1][j] >> 5;
- finalforms[2][j] = tmpforms[2][j] >> 4;
- }
- for(j=0;j<=(divtable[f]>>15);j++) {
- finalforms[3][j] = tmpforms[3][j] >> 3;
- }
-
- int out;
-
- out = fp.write(finalforms[0],divtable[f]>>15);
- out = fp.write(finalforms[1],divtable[f]>>15);
- out = fp.write(finalforms[2],divtable[f]>>15);
- out = fp.write(finalforms[3],divtable[f]>>14);
-
-
- }
- fp.close();
-#endif
-
- return true;
-}
-
-bool CSynthMT32::ClassicOpen(SynthProperties useProp) {
-
-#ifdef NOMANSLAND
-
- if (isOpen) return false;
- int i;
- // Initalize patch information
- uint8 sysexBuf[SYSEX_SIZE];
- uint16 syslen = 0;
-
- bool inSys = false;
-
- File fp;
- uint8 c;
-
- myProp = useProp;
-
- usefilter = &iir_filter_normal;
-
-
- for(i=0;i<MAXPARTIALS;i++) {
- partTable[i] = new CPartialMT32(i);
- }
-
- //pInfo = fopen("partial.nfo","wb");
-
-
- //LOG_MSG("MT-32 Initializing patch banks");
-
- for(initmode=0;initmode<2;initmode++) {
-
- switch(initmode) {
- case 0:
- fp.open("Preset1.syx");
- if(!fp.isOpen()) {
- // TODO : Fail driver init
- error("Unable to open Preset1.syx");
- return false;
- }
- break;
- case 1:
- fp.open("Preset2.syx");
- if(!fp.isOpen()) {
- // TODO : Fail driver init
- error("Unable to open Preset2.syx");
- return false;
- }
- break;
- default:
-
- // TODO : Fail driver init
- return false;
-
- break;
- }
-
- while(!fp.eof()) {
- c = fp.readByte();
- sysexBuf[syslen] = c;
- syslen++;
- if(c==0xf0) {
- inSys = true;
- }
- if ((c==0xf7) && (inSys)) {
- PlaySysex(&sysexBuf[0],syslen);
- inSys = false;
- syslen = 0;
- }
- }
- fp.close();
-
+ for (int i=0;i<54;i++) {
+ PCMReassign[i] = i;
+ PCM[i].tune = 220.0f;
+ PCM[i].ampval = 256;
}
+ //PCM[53].ampval = 128;
- //LOG_MSG("MT-32 Initializing Drums");
-
- File fDrums;
- fDrums.open("drumpat.rom");
- if(!fDrums.isOpen()) {
- // TODO : Fail driver init
- // printf("MT-32 Init Error - Missing drumpat.rom\n");
- error("Unable to open drumpat.rom");
+ char tbuf[512];
+ char *cp;
+ if (!file->readLine(tbuf,sizeof(tbuf))) {
return false;
}
- int drumnum=0;
- while(!fDrums.eof()) {
- //Read common area
- fDrums.read(&drums[drumnum].common,14);
- int t;
- for(t=0;t<4;t++) {
- if (((drums[drumnum].common.pmute >> t) & 0x1) == 0x1) {
- fDrums.read(&drums[drumnum].partial[t],58);
- //LOG_MSG("Loaded drum #%d - t %d", drumnum,t);
+ Bit32u patchstart = 0; //axtoi(tbuf);
+ Bit32u patchend = 0;
+ Bit32u patchcount = 0;
+ bool rc = true;
+ for (;;) {
+ if (!file->readLine(tbuf,sizeof(tbuf))) {
+ if (!file->isEOF()) {
+ rc = false;
}
+ break;
}
- //LOG_MSG("Loaded drum #%d - %s", drumnum,drums[drumnum].common.name);
- drumnum++;
- }
- fDrums.close();
-
-#if DUMPDRUMS == 1
- fp.open("drumsys.syx", File::kFileWriteMode);
- char dumbtext[10], tmpb;
- memset(dumbtext,0,10);
- for(drumnum=0;drumnum<30;drumnum++) {
- // Sysex header
- tmpb = 0xf0; fp.write(&tmpb,1);
- tmpb = 0x41; fp.write(&tmpb,1);
- tmpb = 0x10; fp.write(&tmpb,1);
- tmpb = 0x16; fp.write(&tmpb,1);
- tmpb = 0x12; fp.write(&tmpb,1);
-
- int useaddr = drumnum * 256;
- char lsb = useaddr & 0x7f;
- char isb = (useaddr >> 7) & 0x7f;
- char msb = ((useaddr >> 14) & 0x7f) | 0x08;
- //Address
- fp.write(&msb, 1);
- fp.write(&isb, 1);
- fp.write(&lsb, 1);
- unsigned int checksum = msb + isb + lsb;
-
- //Data
- fp.write(&drums[drumnum].common,0xe);
- fp.write(&drums[drumnum].partial[0],0x3a);
- fp.write(&drums[drumnum].partial[1],0x3a);
- fp.write(&drums[drumnum].partial[2],0x3a);
- fp.write(&drums[drumnum].partial[3],0x3a);
- //Checksum
- char *dat = (char *)&drums[drumnum];
- int ch;
- for(ch=0;ch<246;ch++) checksum += *dat++;
- checksum = (checksum & 0x7f);
- if(checksum) checksum = 0x80 - checksum;
-
- fp.write(&checksum,1);
-
- //End of sysex
- tmpb = 0xf7; fp.write(&tmpb,1);
- }
- fp.close();
-#endif
-
- //LOG_MSG("MT-32 Initializing Partials");
-
- File fPatch;
- fPatch.open("patchlog.cfg");
-
- if(!fPatch.isOpen()) {
- // TODO : Fail driver init
- // printf("MT-32 Init Error - Missing patchlog.cfg\n");
- error("Unable to open patchlog.cfg");
- return false;
- }
-
- for(i=0;i<54;i++) {
- PCMReassign[i] = i;
- PCM[i].tune = 220.0;
- PCM[i].ampval = 256;
-
- }
- //PCM[53].ampval = 128;
- if (!fPatch.eof()) {
- char tbuf[512];
- char *cp;
- fPatch.gets(tbuf,sizeof(tbuf));
- uint32 patchstart = 0; //axtoi(tbuf);
- uint32 patchend = 0;
- uint32 patchcount = 0;
- //int16 *romaddr = &romfile[0];
- while (!fPatch.eof()) {
- fPatch.gets(tbuf,sizeof(tbuf));
- cp = strtok(tbuf," \n\r");
- PCM[patchcount].loop = false;
- if(cp != NULL) {
- patchend = axtoi(cp);
+ cp = strtok(tbuf," \n\r");
+ PCM[patchcount].loop = false;
+ if (cp != NULL) {
+ patchend = axtoi(cp);
+ cp = strtok(NULL," \n\r");
+ if (cp != NULL) {
cp = strtok(NULL," \n\r");
- if(cp != NULL) {
+ if (cp != NULL) {
cp = strtok(NULL," \n\r");
- if(cp != NULL) {
+ if (cp !=NULL) {
+ int newpcm = atoi(cp);
+ PCMReassign[newpcm] = patchcount;
cp = strtok(NULL," \n\r");
- if (cp !=NULL) {
- int newpcm = atoi(cp);
- PCMReassign[newpcm] = patchcount;
+ if (cp != NULL) {
+ if (atoi(cp)==1)
+ PCM[patchcount].loop = true;
cp = strtok(NULL," \n\r");
- if(cp != NULL) {
- if(atoi(cp)==1) PCM[patchcount].loop = true;
- cp = strtok(NULL," \n\r");
- if(cp != NULL) {
- PCM[patchcount].tune = (float)atoi(cp) / 100.0;
- //LOG_MSG("PCM %d tuning at %f", patchcount, PCM[patchcount].tune);
- }
+ if (cp != NULL) {
+ PCM[patchcount].tune = (float)atoi(cp) / 100.0f;
+ //printDebug("PCM %d tuning at %f", patchcount, PCM[patchcount].tune);
}
}
}
}
}
- if (patchend==0) break;
-
- PCM[patchcount].addr = patchstart;
- PCM[patchcount].len = patchend - patchstart;
- patchcount++;
- //printf("Patch %d %d %d %d\n", patchcount, patchstart, patchend, mt32ram.PCM[patchcount].len);
- patchstart = patchend;
-
}
- } else {
- // TODO : Fail driver init
- return false;
+ if (patchend==0)
+ break;
- //exit(0);
+ PCM[patchcount].addr = patchstart;
+ PCM[patchcount].len = patchend - patchstart;
+ patchcount++;
+ //printf("Patch %d %d %d %d", patchcount, patchstart, patchend, mt32ram.PCM[patchcount].len);
+ patchstart = patchend;
}
-
- fPatch.close();
-
+ closeFile(file);
+ if (!rc)
+ return rc;
PCM[53].len = 1950;
- int pat = 0;
- int p;
- for(p=0;p<54;p++) {
- int pr = PCMReassign[p];
- if(!PCM[pr].loop) {
- PCMLoopTable[pat] = p;
- pat++;
- }
- if(pat==7) {
- PCMLoopTable[pat] = p;
- pat++;
- }
- }
- // These are the special combination loop patches
- for(p=46;p<=54;p++) {
- PCMLoopTable[pat] = -(p - 45);
- pat++;
- }
-
- //for(p=0;p<54;p++) LOG_MSG("Loop table %d = %d (%d)", p, PCMLoopTable[p], PCM[p].loop);
-
// Generate official PCM list
// Normal sounds
- pat = 0;
- for(p=0;p<54;p++) {
+ int pat = 0;
+ for (int p = 0; p < 54; p++) {
PCMList[pat].addr = PCM[PCMReassign[p]].addr;
PCMList[pat].len = PCM[PCMReassign[p]].len;
PCMList[pat].loop = PCM[PCMReassign[p]].loop;
PCMList[pat].aggSound = -1;
PCMList[pat].pcmnum = PCMReassign[p];
- PCMList[pat].ampval = PCM[PCMReassign[p]].ampval;
pat++;
}
// Drum specific sounds. Not exactly sure yet how these are different
- for(p=0;p<20;p++) {
+ for (int p = 0; p < 20; p++) {
PCMList[pat] = PCMList[p];
pat++;
}
-
+
// Looped PCM sounds. The last remaining 9 are aggregate sounds;
- for(p=0;p<54;p++) {
- if(PCMLoopTable[p]>-1) {
- PCMList[pat].addr = PCM[PCMReassign[PCMLoopTable[p]]].addr;
- PCMList[pat].ampval = PCM[PCMReassign[PCMLoopTable[p]]].ampval;
- PCMList[pat].len = PCM[PCMReassign[PCMLoopTable[p]]].len;
- PCMList[pat].pcmnum = PCMReassign[PCMLoopTable[p]];
+ // FIXME:KG: I hope this is correct; the original was heavily broken,
+ // and it was hard to determine the author's intention.
+ for (int p = 0; p < 54; p++) {
+ if (p < 45) {
+ int pcmNum = p > 7 ? p - 1 : p;
+ PCMList[pat].addr = PCM[PCMReassign[pcmNum]].addr;
+ PCMList[pat].len = PCM[PCMReassign[pcmNum]].len;
+ PCMList[pat].pcmnum = PCMReassign[pcmNum];
PCMList[pat].loop = true;
PCMList[pat].aggSound = -1;
} else {
- PCMList[pat].addr = 0;
-
//Calculate aggregate length
- int aggsnd = (-PCMLoopTable[p])-1;
+ int aggsnd = p - 45;
int tmplen = 0;
int sndpos = 0;
- while(LoopPatterns[aggsnd][sndpos] != -1) {
+ while (LoopPatterns[aggsnd][sndpos] != -1) {
tmplen += PCM[LoopPatterns[aggsnd][sndpos]].len;
sndpos++;
}
-
+ PCMList[pat].addr = 0;
PCMList[pat].len = tmplen;
PCMList[pat].loop = true;
PCMList[pat].aggSound = aggsnd;
- PCMList[pat].ampval = 0x100;
}
pat++;
-
}
- //for(p=0;p<128;p++) LOG_MSG("PCM #%d addr 0x%x len %d loop %d aggSound %d pcmnum %d", p, PCMList[p].addr, PCMList[p].len, PCMList[p].loop, PCMList[p].aggSound, PCMList[p].pcmnum);
+ //for (p=0;p<128;p++)
+ // printDebug("PCM #%d addr 0x%x len %d loop %d aggSound %d pcmnum %d", p, PCMList[p].addr, PCMList[p].len, PCMList[p].loop, PCMList[p].aggSound, PCMList[p].pcmnum);
+ return true;
+}
- //LOG_MSG("MT-32 Initializing ROM");
- File fIn;
- fIn.open("MT32_PCM.ROM");
-#ifdef MT32OUT
- File fOut, fOutb;
- char tmpc;
- fOut.open("mt32out.raw",File::kFileWriteMode);
- fOutb.open("mt32out2.raw",File::kFileWriteMode);
-#endif
-
- if(!fIn.isOpen()) {
- // TODO : Fail driver init
- // printf("MT-32 Init Error - Missing MT32_PCM.ROM\n");
- error("Unable to open MT32_PCM.ROM");
+bool Synth::loadROM(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read); // ROM File
+ if (file == NULL) {
return false;
}
- i=0;
- //int32 maxamp = 0;
- while (!fIn.eof()) {
- int16 s, c1;
-
- s = fIn.readByte();
- c1 = fIn.readByte();
-
- /*
- int e,z,u,bit;
-
- int order[16] = {0, 9,1 ,2, 3, 4, 5, 6, 7, 10, 11, 12,13, 14, 15,8};
-
- e=0;
- z = 15;
- for(u=0;u<15;u++) {
- if((order[u]<8) && (order[u]>=0)) {
- bit = (s >> (7-order[u])) & 0x1;
+#ifdef MT32OUT
+ File *outFile = openFile("mt32out.raw", File::OpenMode_write);
+ File *outFileB = openFile("mt32out2.raw", File::OpenMode_write);
+#endif
+ bool rc = true;
+ for (int i = 0; ; i++) {
+ int fc = file->readByte();
+ if (fc == -1) {
+ if (!file->isEOF()) {
+ rc = false;
+ }
+ break;
+ }
+ Bit16s s = (Bit16s)fc;
+ fc = file->readByte();
+ if (fc == -1) {
+ if (!file->isEOF()) {
+ rc = false;
} else {
- if(order[u]>=8) {
- bit = (c1 >> (7-(order[u]-8))) & 0x1;
- } else {
- bit = 0;
- }
+ printDebug("ROM file has an odd number of bytes! Ignoring last");
}
- e = e | (bit << z);
- --z;
+ break;
}
-
- //if( (e & 0x1) != 0) printf("Last bit = %d\n", e & 0x1);
- //int16 e = ( ((s & 0x7f) << 4) | ((c1 & 0x40) << 6) | ((s & 0x80) << 6) | ((c1 & 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));*/
+ Bit16s c = (Bit16s)fc;
short e;
- int z;
int bit;
int u;
-
- static const int order[16] = {0, 9,1 ,2, 3, 4, 5, 6, 7, 10, 11, 12,13, 14, 15,8};
-
+
+ int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
+
e=0;
- z = 15;
- for(u=0;u<15;u++) {
- if((order[u]<8) && (order[u]>=0)) {
+ for (u=0;u<15;u++) {
+ if (order[u]<8)
bit = (s >> (7-order[u])) & 0x1;
- } else {
- if(order[u]>=8) {
- bit = (c1 >> (7-(order[u]-8))) & 0x1;
- } else {
- bit = 0;
- }
- }
- e = e | (bit << z);
- --z;
+ 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));
+ */
+
#ifdef MT32OUT
- tmpc = e & 0xff; fOut.write(&tmpc, 1);
- tmpc = (e >> 8) & 0x7f; fOut.write(&tmpc, 1);
-#endif
+ outFile->writeByte(e & 0xff);
+ outFile->writeByte(((e >> 8) & 0x7f));
+#endif
// File is encoded in dB, convert to PCM
// MINDB = -96
// MAXDB = -15
float testval;
testval = (float)((~e) & 0x7fff);
- testval = -(testval / 400.00);
+ testval = -(testval / 400.00f);
//testval = -(testval / 341.32291666666666666666666666667);
- //testval = -(testval / 400.00);
- float vol = pow((float)8,(float)(testval / 20)) * 32767;
-
- if (e>0) vol = -vol;
-
- romfile[i] = (int16)vol;
+ float vol = powf(8,(testval / 20)) * 32767.0f;
-#ifdef MT32OUT
- tmpc = (int16)vol & 0xff; fOutb.write(&tmpc, 1);
- tmpc = (int16)vol >> 8; fOutb.write(&tmpc, 1);
-#endif
+ if (e>0)
+ vol = -vol;
- i++;
+ romfile[i] = (Bit16s)vol;
+
+#ifdef MT32OUT
+ outFileB->writeByte(romfile[i] & 0xff);
+ outFileB->writeByte(romfile[i] >> 8);
+#endif
}
- //LOG_MSG("PCM amp = %d", maxamp);
#ifdef MT32OUT
- fOutb.close();
- fOut.close();
+ closeFile(outFileB);
+ closeFile(outFile);
#endif
- fIn.close();
- int tmp;
- for(tmp=0;tmp<16;tmp++) {
- if((tmp>=1) && (tmp<=9)) {
- chantable[tmp] = tmp-1;
- } else {
- chantable[tmp] = -1;
- }
+ closeFile(file);
+ return rc;
+}
+
+bool Synth::open(SynthProperties &useProp) {
+ if (isOpen)
+ return false;
+
+ // Initalise patch information
+
+ myProp = useProp;
+
+ usefilter = &iir_filter_normal;
+
+ partialManager = new PartialManager(this);
+
+ // This is so that names won't be garbage during early setup debug output, but we can detect bugs
+ memset(&mt32ram, '?', sizeof(mt32ram));
+
+ printDebug("Initialising patch banks");
+ initmode = 0;
+ if (!loadPreset("Preset1.syx")) {
+ report(ReportType_errorPreset1, &errno);
+ return false;
}
- chantable[10] = 8;
- for(i=0;i<128;i++) {
- mt32ram.params.pSettings[i].timbreGroup = i >> 6;
- mt32ram.params.pSettings[i].timbreNum = i & 63;
+ initmode = 1;
+ if (!loadPreset("Preset2.syx")) {
+ report(ReportType_errorPreset2, &errno);
+ return false;
}
+ initmode = 2;
- // For resetting mt32 mid-execution
- memcpy(&mt32default, &mt32ram, sizeof(mt32ram));
+ printDebug("Initialising Drums");
+ if (!loadDrums("drumpat.rom")) {
+ report(ReportType_errorDrumpat, &errno);
+ return false;
+ }
- if (!InitTables()) return false;
- if(myProp.UseDefault) {
- InitReverb(0,5,SETRATE);
- } else {
- InitReverb(myProp.RevType, myProp.RevTime,SETRATE);
+#if DUMPDRUMS == 1
+ strcpy(&pathBuf[0], baseDir);
+ dumpDrums(strcat(&pathBuf[0],"drumsys.syx"));
+#endif
+
+ printDebug("Initialising PCM-to-ROM map");
+ if (!loadPCMToROMMap("patchlog.cfg")) {
+ printDebug("Init Error - Missing patchlog.cfg");
+ report(ReportType_errorPatchlog, &errno);
+ return false;
+ }
+
+ printDebug("Initialising ROM");
+ if (!loadROM("MT32_PCM.ROM")) {
+ printDebug("Init Error - Missing MT32_PCM.ROM");
+ report(ReportType_errorMT32ROM, &errno);
+ return false;
+ }
+ memcpy(chantable, InitChanTable, sizeof (chantable));
+ for (unsigned char i = 0; i < 128; i++) {
+ mt32ram.params.patches[i].timbreGroup = i >> 6;
+ mt32ram.params.patches[i].timbreNum = i & 63;
}
- for(i=0;i<10;i++) {
- mchan[i] = new MidiChannel(SETRATE,i);
+ TableInitialiser tableInitialiser;
+ tableInitialiser.initMT32Tables(this, PCM, (float)myProp.SampleRate);
+ if (myProp.UseDefault)
+ initReverb(0,5);
+ else
+ initReverb(myProp.RevType, myProp.RevTime);
+
+ for (int i = 0; i < 9; i++) {
+ parts[i] = new Part(this, i);
- if(i<8) mchan[i]->SetPatch(InitInstr[i],-1);
- if(i>8) mchan[i]->SetPatch(InitInstr[i-9],-1);
+ if (i<8) {
+ // The patch is already set by the presets, now set the timbre it wants
+ parts[i]->setTimbre(&mt32ram.params.timbres[parts[i]->getAbsTimbreNum()].timbre);
+ // And refresh the part's cache
+ parts[i]->RefreshPatch();
+ }
}
- activeChannels = 0;
+
+ // For resetting mt32 mid-execution
+ mt32default = mt32ram;
#ifdef HAVE_X86
- bool useSSE = false, use3DNow = false;
+ bool availableSSE = DetectSIMD();
+ bool available3DNow = Detect3DNow();
+
+ if (availableSSE)
+ report(ReportType_availableSSE, NULL);
+ if (available3DNow)
+ report(ReportType_available3DNow, NULL);
- use3DNow = Detect3DNow();
- useSSE = DetectSIMD();
-
- if (useSSE) debug(1, "MT-32: SSE detected and enabled");
- if (use3DNow) debug(1, "MT-32: 3DNow! detected and enabled");
-
- if(use3DNow) {
- debug(1, "MT-32 using use SIMD (AMD 3DNow) extensions");
+ if (available3DNow) {
+ printDebug("Detected and using SIMD (AMD 3DNow) extensions");
usefilter = &iir_filter_3dnow;
+ } else if (availableSSE) {
+ printDebug("Detected and using SIMD (Intel SSE) extensions");
+ usefilter = &iir_filter_sse;
}
+#endif
- if(useSSE) {
- debug(1, "MT-32 using SIMD (Intel SSE) extensions\n");
- usefilter = &iir_filter_sse;
- usingSIMD = true;
+#if BENCHMARK > 1
+ // Benchmark 3DNow, Floating point, and SSE filters
+ clock_t start, end;
+ float histval[50];
+
+ for (int bench = 0; bench < 3; bench++) {
+ start = clock();
+ for (int benchcnt=0;benchcnt<2000000;benchcnt++) {
+ switch (bench) {
+ case 0:
+ iir_filter_normal(0,&histval[0],filtcoeff[0][0],0);
+ break;
+ case 1:
+ if (!availableSSE) {
+ printDebug("Skipping SSE benchmark, SSE not available");
+ continue;
+ }
+ iir_filter_sse(0,&histval[0],filtcoeff[0][0],0);
+ break;
+ case 2:
+ if (!available3DNow) {
+ printDebug("Skipping 3DNow benchmark, 3DNow not available");
+ continue;
+ }
+ iir_filter_3dnow(0,&histval[0],filtcoeff[0][0],0);
+ break;
+ }
+ }
+ end = clock();
+ printDebug("Bench %ld completed in %ld milliseconds", bench, (end - start) * 1000 / CLOCKS_PER_SEC);
}
#endif
isOpen=true;
isEnabled=false;
-#endif
-
+ printDebug("**************** Initialisation complete ****************");
return true;
-}
+};
-void CSynthMT32::Close(void) {
- if (!isOpen) return;
+void Synth::close(void) {
+ if (!isOpen)
+ return;
-#ifdef NOMANSLAND
- int t, m;
- for(t=0;t<4;t++) {
- for(m=0;m<256;m++) {
- if(waveforms[t][m]!=NULL) free(waveforms[t][m]);
+ for (int t = 0; t < 4; t++) {
+ for (int m = 0; m < 128; m++) {
+ if (waveforms[t][m]!=NULL) {
+ delete[] waveforms[t][m];
+ waveforms[t][m] = NULL;
+ }
}
}
+ if (partialManager != NULL) {
+ partialManager = NULL;
+ delete partialManager;
+ }
-#endif
-
- isOpen=false;
+ if (reverbModel != NULL) {
+ reverbModel = NULL;
+ delete reverbModel;
+ }
+ for (int i = 0; i < 9; i++) {
+ if (parts[i] != NULL) {
+ delete parts[i];
+ parts[i] = NULL;
+ }
+ }
-}
+ isOpen=false;
+};
-void CSynthMT32::PlayMsg(uint32 msg) {
+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;
-#ifdef NOMANSLAND
+ //if (code!=0xf) printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
- int h;
- int code = msg & 0xf0;
- int chan = msg & 0xf;
- int note = (msg & 0xff00) >> 8;
- int velocity = (msg & 0xff0000) >> 16;
- isEnabled= true;
-
- //if(chan!=0x9) {
- // if(chan==12) return;
- // chan = chan & 0x7;
- //
- //} else {
- // chan = 8;
- //}
- //if (chan==0) return;
- //int prechan = chan;
- //if(code!=0xf0) LOG_MSG("Playing chan %d, code 0x%x note: 0x%x", chan, code, note);
-
- chan = chantable[chan];
- //LOG_MSG("Play msg on chan: %d = %d note: %x velocity: %x", chan, msg & 0xf, note, velocity);
- if(chan<0) {
- //LOG_MSG("Play msg 0x%x on unreg chan: %d = %x", chan, msg & 0xf);
+ 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;
-
}
- if(chan>8) return;
-
+ playMsgOnPart(part, code, note, velocity);
+}
- int patch;
- uint32 bend;
+void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
+ Bit32u bend;
- //LOG_MSG("Midi code: 0x%x",msg);
+ //printDebug("Synth::playMsg(0x%02x)",msg);
switch (code) {
- case 0x80:
- //LOG_MSG("Note OFF - Channel %d",chan);
- mchan[chan]->StopNote(note,velocity);
+ case 0x8:
+ //printDebug("Note OFF - Part %d", part);
+ // The MT-32 ignores velocity for note off
+ parts[part]->StopNote(note);
break;
- case 0x90:
- //if(chan!=4) return;
- //LOG_MSG("Note ON - Channel %d, Note %d Vel %d",chan, note, velocity);
-
- if(velocity>0) {
- mchan[chan]->PlayNote(note,velocity);
+ 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 {
- mchan[chan]->StopNote(note,velocity);
+ parts[part]->PlayNote(partialManager, note, velocity);
}
-
-
break;
- case 0xb0: // Control change
+ case 0xB: // Control change
switch (note) {
- case 0x1: // Modulation
- //LOG_MSG("Modulation: %d", velocity);
- mchan[chan]->SetModulation(velocity);
+ case 0x01: // Modulation
+ //printDebug("Modulation: %d", velocity);
+ parts[part]->SetModulation(velocity);
break;
- case 0xb:
- //LOG_MSG("Expression set: %d", velocity);
- mchan[chan]->SetVolume(velocity);
+ case 0x0B:
+ //printDebug("Expression set: %d", velocity);
+ parts[part]->SetVolume(velocity);
break;
- case 0x7: // Set volume
- //if(chan!=3) return;
- //LOG_MSG("Volume set: %d", velocity);
- mchan[chan]->SetVolume(velocity);
+ case 0x07: // Set volume
+ //if (part!=3) return;
+ //printDebug("Volume set: %d", velocity);
+ parts[part]->SetVolume(velocity);
break;
- case 0xa: // Pan
- mchan[chan]->SetPan(velocity);
+ case 0x0A: // Pan
+ //printDebug("Pan set: %d", velocity);
+ parts[part]->SetPan(velocity);
break;
case 0x40: // Hold pedal
- if(velocity<64) {
- mchan[chan]->SetHoldPedal(false);
- mchan[chan]->StopPedalHold();
- } else {
- mchan[chan]->SetHoldPedal(true);
- }
+ //printDebug("Hold pedal set: %d", velocity);
+ parts[part]->SetHoldPedal(velocity>=64);
break;
- case 0x7b: // All notes off
-
- for(h=0;h<MAXPOLY;h++) {
- mchan[chan]->StopNote(-1,0);
- }
+ case 0x7B: // All notes off
+ //printDebug("All notes off");
+ parts[part]->AllStop();
break;
case 0x79: // Reset all controllers
+ printDebug("Reset all controllers (NYI)");
break;
default:
- //LOG_MSG("Control code: 0x%x - vel %x",note, velocity);
+ printDebug("Unknown MIDI Control code: 0x%02x - vel %02x",note, velocity);
break;
}
break;
- case 0xc0: // Patch setting
- char currentInstr[32];
- patch = (mt32ram.params.pSettings[note].timbreGroup * 64) + mt32ram.params.pSettings[note].timbreNum;
- memset(&currentInstr,0,16);
- memcpy(&currentInstr,mt32ram.params.patch[patch].common.name,10);
-
- //LOG_MSG("Set patch (%s) %d (%d) chan %d (%d) from ng %d, t %d", currentInstr, patch, note, chan, msg & 0xf, mt32ram.params.pSettings[note].timbreGroup, mt32ram.params.pSettings[note].timbreNum);
- if((chan>=0) && (chan<8)) mt32ram.params.timTemp[chan] = mt32ram.params.patch[patch];
- mchan[chan]->SetPatch(note,-1);
+ case 0xC: // Program change
+ //printDebug("Program change %01x", note);
+ if (part < 8) {
+ parts[part]->SetPatch(note);
+ } else {
+ printDebug("Program change attempted on rhythm part");
+ }
break;
- case 0xe0: // Pitch bender
+ case 0xE: // Pitch bender
bend = (velocity << 7) | (note);
- //LOG_MSG("Pitch bender %x", bend);
- mchan[chan]->SetBend(bend);
+ //printDebug("Pitch bender %02x", bend);
+ parts[part]->SetBend(bend);
break;
default:
- //LOG_MSG("Undef Midi code: 0x%x - %x - %x",code, note, velocity);
-
+ printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
break;
}
-#endif
//midiOutShortMsg(m_out, msg);
+};
+
+void Synth::playSysex(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 CSynthMT32::PlaySysex(uint8 * sysex,uint32 len) {
+void Synth::playSysexWithoutFraming(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);
+}
-#ifdef NOMANSLAND
+// 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))
- uint32 addr;
- uint32 *header;
- unsigned int off;
- int m;
- header = (uint32 *)(sysex+1);
- //int dummy = 0;
- int32 lens = len;
+#define NUMTOUCHED(x,y) (((x) + sizeof(y) - 1) / sizeof(y))
- // HACK: For some reason commands in IMuseInternal::initMT32 do not have prefix byte
- // Also in some cases, particularly in mi2 "glop" sound at difficulty select screen
- // header is wrong. I don't know what causes this as original has neither of these
- // problems.
- if((READ_BE_UINT32(header) != 0x41101612) || (READ_BE_UINT32(header) == 0x41001612)) {
- if((READ_LE_UINT32(sysex) == 0x41101612) || (READ_BE_UINT32(sysex) == 0x41001612)) {
- header = (uint32 *)sysex;
- sysex--; // We don't access sysex[0], so it's safe
- }
+void Synth::playSysexWithoutHeader(unsigned char device, 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(READ_BE_UINT32(header) == 0x41101612) {
- addr = (sysex[5] << 16) | (sysex[6] << 8) | (sysex[7]);
- //LOG_MSG("Sysex addr: %x", addr);
- if (addr<0x30000) {
- //LOG_MSG("Channel temp area %x", addr);
- }
- if ((addr>=0x30000) && (addr<0x40000)) {
- off = ((addr & 0x7f00) >> 1) | (addr & 0xff);
- for(m=0;m<(lens-10);m++) {
- mt32ram.memabs.mt32memory[off+m] = sysex[8+m];
+ if (len < 4) {
+ printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
+ return;
+ }
+ unsigned char checksum = calcChecksum(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);
}
- //LOG_MSG("Patch temp %d at %x - len %d", off/16, off % 16, len-10);
-
- if(initmode>1) {
- for(m=0;m<8;m++) {
- int tmppat;
- for(tmppat=0;tmppat<128;tmppat++) {
- if(mt32ram.params.pSettings[tmppat].timbreGroup == mt32ram.params.tmpSettings[m].timbreGroup) {
- if(mt32ram.params.pSettings[tmppat].timbreNum == mt32ram.params.tmpSettings[m].timbreNum) {
- //LOG_MSG("Setting %d to patch %d", m, tmppat);
- this->PlayMsg((tmppat << 8) | 0xc0 | m);
-
- break;
- }
- }
+ 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.banks.pTemp)) {
+ 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++)
+ mt32ram.banks.pTemp[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.params.patchSettings[i].patch.timbreGroup * 64 + mt32ram.params.patchSettings[i].patch.timbreNum;
+ char timbreName[11];
+ memcpy(timbreName, mt32ram.params.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) {
+ parts[i]->setTimbre(&mt32ram.params.timbres[parts[i]->getAbsTimbreNum()].timbre);
+ parts[i]->RefreshPatch();
+ }
+ }
+ } else if (addr >= MEMADDR(0x030110) && addr < MEMADDR(0x040000)) {
+ int off = addr - MEMADDR(0x030110);
+ if (off + len > sizeof(mt32ram.banks.rTemp)) {
+ 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++)
+ mt32ram.banks.rTemp[firstDrum][off + m] = sysex[m];
+ int lastDrum = firstDrum + NUMTOUCHED(off + len, MemParams::RhythmTemp) - 1;
+ for (int i = firstDrum; i <= lastDrum; i++) {
+ int timbreNum = mt32ram.params.rhythmSettings[i].timbre;
+ char timbreName[11];
+ if (timbreNum < 94) {
+ memcpy(timbreName, mt32ram.params.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.params.rhythmSettings[i].outlevel, mt32ram.params.rhythmSettings[i].panpot, mt32ram.params.rhythmSettings[i].reverbSwitch, mt32ram.params.rhythmSettings[i].timbre, timbreName);
+ }
+ if (parts[8] != NULL) {
+ parts[8]->RefreshDrumCache();
+ }
+ } else if (addr >= MEMADDR(0x040000) && addr < MEMADDR(0x050000)) {
+ int off = addr - MEMADDR(0x040000);
+ if (off + len > sizeof(mt32ram.banks.tTemp)) {
+ 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++)
+ mt32ram.banks.tTemp[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.params.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]->RefreshPatch();
+ }
+ }
+ }
+ else if (addr >= MEMADDR(0x050000) && addr < MEMADDR(0x060000)) {
+ int off = addr - MEMADDR(0x050000);
+ if (off + len > sizeof(mt32ram.banks.patchBank)) {
+ 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++)
+ mt32ram.banks.patchBank[firstPatch][off + m] = sysex[m];
+ int lastPatch = firstPatch + NUMTOUCHED(off + len, PatchParam) - 1;
+ for (int i = firstPatch; i <= lastPatch; i++) {
+ PatchParam *patch = &mt32ram.params.patches[i];
+ int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.params.timbres[patchAbsTimbreNum].timbre.common.name, 10);
+ instrumentName[10] = 0;
+ printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s)", firstPatch, lastPatch, off, off + len, i, patchAbsTimbreNum, instrumentName);
+ // 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.params.patchSettings[part].patch.timbreGroup * 64 + mt32ram.params.patchSettings[part].patch.timbreNum;
+ if (partPatchAbsTimbreNum == patchAbsTimbreNum) {
+ parts[part]->setPatch(patch);
+ parts[part]->RefreshPatch();
}
- //LOG_MSG("Patch chan %d - Assign mode %d", m,mt32ram.params.tmpSettings[m].fineTune);
}
}
+ */
}
- if ((addr>=0x40000) && (addr<0x50000)) {
- int toffat = sizeof(mt32ram.patchabs.pTemp) + sizeof(mt32ram.patchabs.rTemp);
- off = ((addr & 0x7f00) >> 1) | (addr & 0x7f);
- for(m=0;m<(lens-10);m++) {
- mt32ram.memabs.mt32memory[off+m+toffat] = sysex[8+m];
- }
- int chanoff = off / sizeof(timbreParam);
-
- //LOG_MSG("Timbre temp off %x offdiv %x - len %d", off, chanoff, len-10);
- if(mchan[chanoff]!=NULL) mchan[chanoff]->RefreshPatch();
- }
-
- if ((addr>=0x50000) && (addr<0x60000)) {
- off = (((addr & 0x7f00) >> 1) | (addr & 0xff)) + sizeof(mt32ram.patchabs.pTemp) + sizeof(mt32ram.patchabs.rTemp) + sizeof(mt32ram.patchabs.tTemp);
-
- for(m=0;m<(lens-10);m++) {
- mt32ram.memabs.mt32memory[off+m] = sysex[8+m];
- }
- //LOG_MSG("Patch area %d, assigning to %d, patch %d - len %d", off/16, mt32ram.params.pSettings[off/16].timbreGroup, mt32ram.params.pSettings[off/16].timbreNum, len-10);
+ } else if (addr >= MEMADDR(0x080000) && addr < MEMADDR(0x090000)) {
+ // Timbre patches
+ 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;
}
- if ((addr>=0x80000) && (addr<0x90000)) {
- // Timbre patches
- int tc = (addr & 0xfe00) >> 9;
- off = ((addr & 0x100) >> 1) | (addr & 0xff);
- int calcoff;
- int pn=0;
-
- switch(initmode) {
+ unsigned int firstTimbre = off / sizeof (MemParams::PaddedTimbre);
+ off %= sizeof (MemParams::PaddedTimbre);
+ switch (initmode) {
case 0:
- calcoff = tc * sizeof(timbreParam);
- pn = tc;
+ // Write into first built-in timbre group
break;
case 1:
- calcoff = (tc+ 64) * sizeof(timbreParam);
- pn = tc + 64;
+ // Write into second built-in timbre group
+ firstTimbre += 64;
break;
default:
- calcoff = (tc + 128) * sizeof(timbreParam);
- pn = tc + 128;
- break;
- }
-
- // Transfer sysex parameter data into MT-32 memory
- calcoff += (off + sizeof(mt32ram.patchabs.pTemp) + sizeof(mt32ram.patchabs.rTemp) + sizeof(mt32ram.patchabs.tTemp) + sizeof(mt32ram.patchabs.patchmemory));
- for(m=0;m<(lens-10);m++) {
- mt32ram.memabs.mt32memory[calcoff+m] = sysex[8+m];
+ firstTimbre += 128;
+ // Write into user timbre group
+ }
+ for (unsigned int m = 0; m < len; m++)
+ mt32ram.banks.timbreBank[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.params.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?
+ if (i >= 128 && parts[8] != NULL) {
+ // FIXME:KG: Only bother to re-cache when this timbre's actually in the rhythm map
+ parts[8]->SetPatch(i); // Re-cache this timbre
+ }
+ for (unsigned int part = 0; part < 8; part++) {
+ if (parts[part] != NULL) {
+ if (parts[part]->getAbsTimbreNum() == i) {
+ parts[part]->RefreshPatch();
+ }
+ }
}
- //LOG_MSG("Loaded patch %s at pn %d", mt32ram.params.patch[pn].common.name, pn);
}
- if ((addr>=0x100000) && (addr<=0x1fffff)) {
- off = ((addr & 0x100) >> 1) | (addr & 0xff);
- for(m=0;m<(lens-10);m++) {
- mt32ram.patchabs.systemBank[m+off] = sysex[8+m];
- }
-
- //LOG_MSG("System Reconfiguration:");
- memset(chantable,-1,sizeof(chantable));
- memset(miditable,-1,sizeof(miditable));
+ } else if (addr >= MEMADDR(0x100000) && addr < MEMADDR(0x200000)) {
+ int off = addr - MEMADDR(0x100000);
+ if (off + len > sizeof(mt32ram.banks.systemBank)) {
+ 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++)
+ mt32ram.banks.systemBank[m + off] = sysex[m];
- for(m=0;m<9;m++) {
- //LOG_MSG("Channel %d set to MIDI channel %d",m,mt32ram.params.system.chanAssign[m]);
- if(mt32ram.params.system.chanAssign[m]==16) {
- mchan[m]->AllStop();
- } else {
- chantable[(int)mt32ram.params.system.chanAssign[m]]=m;
- miditable[m] = mt32ram.params.system.chanAssign[m];
- }
- }
+ report(ReportType_devReconfig, NULL);
- //LOG_MSG("Master Tune: %f", ((float)mt32ram.params.system.masterTune)*0.2+432.1);
- //LOG_MSG("Reverb mode: %d", mt32ram.params.system.reverbMode);
- //LOG_MSG("Reverb time: %d", mt32ram.params.system.reverbTime);
- //LOG_MSG("Reverb level: %d", mt32ram.params.system.reverbLevel);
-
- if(((uint32)mt32ram.params.system.reverbMode != curRevMode) || ((uint32)mt32ram.params.system.reverbTime!=curRevTime)) {
- if(myProp.UseDefault) {
- InitReverb(mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime,SETRATE);
- curRevLevel = mt32ram.params.system.reverbLevel;
- } else {
- InitReverb(myProp.RevType, myProp.RevTime,SETRATE);
- curRevLevel = myProp.RevLevel;
- }
- }
+ printDebug("System Reconfiguration:");
+ memset(chantable,-1,sizeof(chantable));
-
- char *rset = mt32ram.params.system.reserveSettings;
- //LOG_MSG("Partial reserve: 1=%d 2=%d 3=%d 4=%d 5=%d 6=%d 7=%d 8=%d 9=%d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
- int x,y,pr;
- pr = 0;
- for(x=0;x<9;x++) {
- for(y=0;y<rset[x];y++) {
- PartialReserveTable[pr] = x;
- pr++;
- }
+ for (unsigned int i = 0; i < 9; i++) {
+ //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.params.system.chanAssign[i]);
+ if (mt32ram.params.system.chanAssign[i] == 16) {
+ parts[i]->AllStop();
+ } else {
+ chantable[(int)mt32ram.params.system.chanAssign[i]] = (char)i;
}
- //if(pr != 32) LOG_MSG("Partial Reserve Table with less than 32 partials reserved!");
- rset = mt32ram.params.system.chanAssign;
- //LOG_MSG("Chan assign: 1=%d 2=%d 3=%d 4=%d 5=%d 6=%d 7=%d 8=%d 9=%d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
- //LOG_MSG("Master volume: %d",mt32ram.params.system.masterVol);
- int16 tv = (int16)((float)mt32ram.params.system.masterVol * 327.0);
- mastervolume = tv;
-
- }
- if (addr==0x200000) {
- char buf[SYSEX_SIZE];
- memset(&buf,0,SYSEX_SIZE);
- memcpy(&buf,&sysex[8],lens-10);
- //LOG_MSG("MT-32 LCD Display: %s", buf);
- g_system->displayMessageOnOSD(buf);
- }
- if ((addr & 0xff0000) == 0x7f0000) {
- //LOG_MSG("MT-32 Reset");
- for (uint32 m1=0;m1<MAXPARTIALS;m1++) partTable[m1]->isActive = false;
-
- memcpy(&mt32ram, &mt32default, sizeof(mt32ram));
- isEnabled = false;
}
+ printDebug(" Master Tune: %f", ((float)mt32ram.params.system.masterTune)*0.2+432.1);
+ printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime, mt32ram.params.system.reverbLevel);
+ report(ReportType_newReverbMode, &mt32ram.params.system.reverbMode);
+ report(ReportType_newReverbTime, &mt32ram.params.system.reverbTime);
+ report(ReportType_newReverbLevel, &mt32ram.params.system.reverbLevel);
+ if ((mt32ram.params.system.reverbMode != curRevMode) || (mt32ram.params.system.reverbTime != curRevTime)) {
+ if (myProp.UseDefault) {
+ initReverb(mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime);
+ curRevLevel = mt32ram.params.system.reverbLevel;
+ } else {
+ initReverb(myProp.RevType, myProp.RevTime);
+ curRevLevel = myProp.RevLevel;
+ }
+ }
+
+ char *rset = mt32ram.params.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.params.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.params.system.masterVol);
+ mastervolume = (Bit16s)((float)mt32ram.params.system.masterVol * 327.0);
+ } else if (addr == MEMADDR(0x200000)) {
+ char buf[SYSEX_SIZE];
+ memset(&buf, 0, SYSEX_SIZE);
+ memcpy(&buf, &sysex[0], len);
+ printDebug("LCD Display: %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 < 8; i++) {
+ parts[i]->RefreshPatch();
+ }
+ parts[8]->RefreshDrumCache();
+ isEnabled = false;
} else {
- // Header not intended for Roland MT-32
+ printDebug("Sysex write to unrecognised address %06x", SYSEXMEMADDR(addr));
}
+};
-#endif
-
-}
-
-
-int CSynthMT32::DumpSysex(char *filename) {
- File fp;
- byte tmpc;
- fp.open(filename,File::kFileWriteMode);
- if(!fp.isOpen())
+int Synth::dumpSysex(char *filename) {
+ File *file = openFile(filename, File::OpenMode_write);
+ if (file == NULL)
return -1;
-
+
int patchnum;
- for(patchnum=0;patchnum<64;patchnum++) {
+ for (patchnum=0;patchnum<64;patchnum++) {
// Sysex header
- tmpc = 0xf0; fp.write(&tmpc, 1);
- tmpc = 0x41; fp.write(&tmpc, 1);
- tmpc = 0x10; fp.write(&tmpc, 1);
- tmpc = 0x16; fp.write(&tmpc, 1);
- tmpc = 0x12; fp.write(&tmpc, 1);
-
+ if (!file->writeByte(0xF0))
+ break;
+ if (!file->writeByte(0x41))
+ break;
+ if (!file->writeByte(0x10))
+ break;
+ if (!file->writeByte(0x16))
+ break;
+ if (!file->writeByte(0x12))
+ break;
+
int useaddr = patchnum * 256;
- char lsb = useaddr & 0x7f;
- char isb = (useaddr >> 7) & 0x7f;
- char msb = ((useaddr >> 14) & 0x7f) | 0x08;
+ char lsb = (char)(useaddr & 0x7f);
+ char isb = (char)((useaddr >> 7) & 0x7f);
+ char msb = (char)(((useaddr >> 14) & 0x7f) | 0x08);
//Address
- fp.write(&msb, 1);
- fp.write(&isb, 1);
- fp.write(&lsb, 1);
- unsigned int checksum = msb + isb + lsb;
-
+ if (!file->writeByte(msb))
+ break;
+ if (!file->writeByte(isb))
+ break;
+ if (!file->writeByte(lsb))
+ break;
+
//Data
- fp.write(&mt32ram.params.patch[patchnum+128].common,0xe);
- fp.write(&mt32ram.params.patch[patchnum+128].partial[0],0x3a);
- fp.write(&mt32ram.params.patch[patchnum+128].partial[1],0x3a);
- fp.write(&mt32ram.params.patch[patchnum+128].partial[2],0x3a);
- fp.write(&mt32ram.params.patch[patchnum+128].partial[3],0x3a);
+ if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.common,0xE) != 0xE)
+ break;
+ if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[0],0x3A) != 0x3A)
+ break;
+ if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[1],0x3A) != 0x3A)
+ break;
+ if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[2],0x3A) != 0x3A)
+ break;
+ if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[3],0x3A) != 0x3A)
+ break;
//Checksum
- char *dat = (char *)&mt32ram.params.patch[patchnum+128];
- int ch;
- for(ch=0;ch<246;ch++) checksum += *dat++;
- checksum = (checksum & 0x7f);
- if(checksum) checksum = 0x80 - checksum;
-
- fp.write(&checksum,1);
-
+ unsigned char *dat = (unsigned char *)&mt32ram.params.timbres[patchnum + 128].timbre;
+ unsigned char checksum = calcChecksum(dat, 246, msb + isb + lsb);
+
+ if (!file->writeByte(checksum))
+ break;
+
//End of sysex
- tmpc = 0xf7; fp.write(&tmpc, 1);
+ if (!file->writeByte(0xF7))
+ break;
}
- fp.close();
- //LOG_MSG("Wrote temp patches to %s", usefile);
-
+ closeFile(file);
+ printDebug("Wrote temp patches to %s", filename);
return 0;
}
-
-
-static int16 tmpBuffer[4096];
-static float sndbufl[4096];
-static float sndbufr[4096];
-static float outbufl[4096];
-static float outbufr[4096];
-
-#if USE_MMX == 3
-static float multFactor[4] = { 32767.0, 32767.0, 32767.0, 32767.0 };
+void ProduceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
+#if 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
-
-void CSynthMT32::MT32_CallBack(uint8 * stream,uint32 len, int volume) {
-
-#ifdef NOMANSLAND
- int32 i,m;
- int16 *snd, *useBuf;
- uint32 outlen;
- snd = (int16 *)stream;
- memset(stream,0,len*4);
- if(!isEnabled) return;
- useBuf = snd;
-
- outlen = len;
-
- assert(len < 1024); // tmpBuffer is 4096 bytes
- /*
- partUsage outUsage;
- for(i=0;i<32;i++) {
- if(partTable[i]->isActive) {
- outUsage.active[i] = -1;
- } else {
- outUsage.active[i] = 0;
- }
- outUsage.owner[i] = partTable[i]->ownerChan;
- outUsage.assign[i] = PartialReserveTable[i];
+ int end = len * 2;
+ while (end--) {
+ *stream = *stream + (Bit16s)(((Bit32s)*useBuf++ * (Bit32s)volume)>>15);
+ stream++;
}
- fwrite(&outUsage,sizeof(outUsage),1,pInfo);*/
-
- for(i=0;i<MAXPARTIALS;i++) partTable[i]->age++;
+}
- for(i=0;i<MAXPARTIALS;i++) {
+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;
+ }
+}
- if(partTable[i]->produceOutput(tmpBuffer,outlen)==true) {
-#if USE_MMX == 0
- int16 *tmpoff = snd;
- int q = 0;
- for(m=0;m<(int32)outlen;m++) {
- tmpoff[q] += (int16)(((int32)tmpBuffer[q] * (int32)mastervolume)>>15);
- q++;
- tmpoff[q] += (int16)(((int32)tmpBuffer[q] * (int32)mastervolume)>>15);
- q++;
+void Synth::doRender(Bit16s * stream,Bit32u len) {
+ Bit32u m;
- }
-#else
- int tmplen = (outlen >> 1) + 4;
-#ifdef I_ASM
- __asm {
- mov ecx, tmplen
- mov ax,mastervolume
- shl eax,16
- mov ax,mastervolume
- movd mm3,eax
- movd mm2,eax
- psllq mm3, 32
- por mm3,mm2
- mov esi, useBuf
- mov edi, snd
-mixloop4:
- movq mm1, [esi]
- movq mm2, [edi]
- pmulhw mm1, mm3
- paddw mm1,mm2
- movq [edi], mm1
-
- add esi,8
- add edi,8
+ partialManager->AgeAll();
- dec ecx
- cmp ecx,0
- jg mixloop4
- emms
+ if (myProp.UseReverb) {
+ bool hasOutput = false;
+ for (unsigned int i = 0; i < MAXPARTIALS; i++) {
+ if (partialManager->shouldReverb(i)) {
+ if (partialManager->ProduceOutput(i, &tmpBuffer[0], len)) {
+ ProduceOutput1(&tmpBuffer[0], stream, len, mastervolume);
+ hasOutput = true;
+ }
}
-#else
- atti386_produceOutput1(tmplen, mastervolume, useBuf, snd);
-#endif
-#endif
}
- }
-
- if(myProp.UseReverb) {
-#if USE_MMX == 3
- if(!usingSIMD) {
-#endif
+ // No point in doing reverb on a mute buffer...
+ if (hasOutput) {
m=0;
- for(i=0;i<(int32)len;i++) {
- sndbufl[i] = (float)snd[m] / 32767.0;
+ for (unsigned int i = 0; i < len; i++) {
+ sndbufl[i] = (float)stream[m] / 32767.0f;
m++;
- sndbufr[i] = (float)snd[m] / 32767.0;
+ sndbufr[i] = (float)stream[m] / 32767.0f;
m++;
}
- newReverb->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
+ reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
m=0;
- for(i=0;i<(int32)len;i++) {
- snd[m] = (int16)(outbufl[i] * 32767.0);
+ for (unsigned int i = 0; i < len; i++) {
+ stream[m] = (Bit16s)(outbufl[i] * 32767.0f);
m++;
- snd[m] = (int16)(outbufr[i] * 32767.0);
+ stream[m] = (Bit16s)(outbufr[i] * 32767.0f);
m++;
}
-#if USE_MMX == 3
- } else {
-#ifdef I_ASM
- // Use SIMD instructions to quickly convert between integer and floating point
- __asm {
- mov ecx, len
- shr ecx, 1
- add ecx, 4
- push ecx
-
- mov esi, multFactor
- movups xmm1, [esi]
-
- // One speaker at a time
- mov esi, snd
- mov edi, sndbufl
-
-convloop1:
- xor eax,eax
- mov ax,[snd]
- cwde // Sign extended ax
- inc snd
- inc snd
- movd mm1,eax
- psrlq mm1, 32
- mov ax,[snd]
- inc snd
- inc snd
- movd mm2,eax
- por mm1,mm2
-
- dec ecx
- jnz convloop1
-
- pop ecx
- mov esi, snd
- mov edi, sndbufr
- inc esi
-convloop2:
-
- dec ecx
- jnz convloop2
-
- }
-#else
- atti386_produceOutput2(len, snd, sndbufl, sndbufr, multFactor);
-#endif
}
-#endif
- }
-
- // for(i=0;i<9;i++) { mchan[i]->CheckNoteList(); }
-
- for(i=0;i<MAXPARTIALS;i++) { partTable[i]->alreadyOutputed = false; }
-
-
-#if MAXPARTIALS == 0
- // Reorganize partials
- CPartialMT32 *tmpPartial;
- int y;
-resetSearch:
- for(i=0;i<MAXPARTIALS;i++) {
- // Try to relocate partials not assigned to the right channel
- if((partTable[i]->isActive) && (partTable[i]->ownerChan != PartialReserveTable[i])) {
- for(y=0;y<MAXPARTIALS;y++) {
- if((!partTable[y]->isActive) && (PartialReserveTable[y] == partTable[i]->ownerChan)) {
- // Found a free channel that can use this partial - swap it out
- tmpPartial = partTable[y];
- partTable[y] = partTable[i];
- partTable[i] = tmpPartial;
- goto resetSearch;
+ for (unsigned int i = 0; i < MAXPARTIALS; 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 < MAXPARTIALS; i++) {
+ if (partialManager->ProduceOutput(i, &tmpBuffer[0], len))
+ ProduceOutput1(&tmpBuffer[0], stream, len, mastervolume);
+ }
}
-#endif
+
+ partialManager->ClearAlreadyOutputed();
#if MONITORPARTIALS == 1
- samplepos+=outlen;
- if(samplepos>SETRATE*5) {
+ samplepos += len;
+ if (samplepos > myProp.SampleRate * 5) {
samplepos = 0;
- int partUse[9];
- memset(partUse,0,sizeof(partUse));
- for(i=0;i<MAXPARTIALS;i++) {
- if(partTable[i]->isActive) partUse[partTable[i]->ownerChan]++;
- }
- //LOG_MSG("C1: %d C2: %d C3: %d C4 %d", partUse[0], partUse[1], partUse[2], partUse[3]);
- //LOG_MSG("C5: %d C6: %d C7: %d C8 %d", partUse[4], partUse[5], partUse[6], partUse[7]);
- //LOG_MSG("Rythmn: %d", partUse[8]);
-
+ 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], MAXPARTIALS - partialManager->GetFreePartialCount());
}
-
#endif
-
-
-#endif
-
+}
}
diff --git a/backends/midi/mt32/synth.h b/backends/midi/mt32/synth.h
index da2c685d34..e181c4d763 100644
--- a/backends/midi/mt32/synth.h
+++ b/backends/midi/mt32/synth.h
@@ -1,68 +1,73 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
*
- * 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.
+ * 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:
*
- * $Header$
+ * 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 CSYNTHMT32_H
-#define CSYNTHMT32_H
-
-#ifdef HAVE_X86
-#if defined(_MSC_VER)
-#define USE_MMX 2
-#define I_ASM
-#else
-#define USE_MMX 0
-#undef I_ASM
-#endif
-#else
-#define USE_MMX 0
-#endif
-
-#define AMPENV 0
-#define FILTENV 1
-#define PITCHENV 2
-
-// Filter setting
-#define FILTER_FLOAT 1
-#define FILTER_64BIT 0
-#define FILTER_INT 0
-
-#define FILTERGRAN 512
-
-// Amplitude of waveform generator
-#define WGAMP (7168)
-//#define WGAMP (8192)
-
-#include "backends/midi/mt32/structures.h"
-#include "sound/mixer.h"
-
-// Function that detects the availablity of SSE SIMD instructions
-// On non-MSVC compilers it automatically returns FALSE as inline assembly is required
-bool DetectSIMD();
-// Function that detects the availablity of 3DNow instructions
-// On non-MSVC compilers it automatically returns FALSE as inline assembly is required
-bool Detect3DNow();
+#ifndef MT32EMU_SYNTH_H
+#define MT32EMU_SYNTH_H
+
+#include <stdarg.h>
+
+// Shows number of partials MT-32 is playing
+#define MONITORPARTIALS 0
+
+#define ROMSIZE (512 * 1024)
+#define PCMSIZE (ROMSIZE / 2)
+#define GRAN 512
+
+class revmodel;
+
+namespace MT32Emu {
+
+class File;
+class TableInitialiser;
+class Partial;
+class PartialManager;
+class Part;
+
+enum ReportType {
+ // Files missing
+ ReportType_errorPreset1 = 1,
+ ReportType_errorPreset2 = 2,
+ ReportType_errorDrumpat = 3,
+ ReportType_errorPatchlog = 4,
+ ReportType_errorMT32ROM = 5,
+
+ // HW spec
+ ReportType_availableSSE = 6,
+ ReportType_available3DNow = 7,
+ ReportType_asingSSE = 8,
+ ReportType_asing3DNow = 9,
+
+ // General info
+ ReportType_lcdMessage = 10,
+ ReportType_devReset = 11,
+ ReportType_devReconfig = 12,
+ ReportType_newReverbMode = 13,
+ ReportType_newReverbTime = 14,
+ ReportType_newReverbLevel = 15
+};
struct SynthProperties {
// Sample rate to use in mixing
int SampleRate;
+
// Flag to activate reverb. True = use reverb, False = no reverb
bool UseReverb;
// Flag True to use software set reverb settings, Flag False to set reverb settings in
@@ -70,11 +75,25 @@ struct SynthProperties {
bool UseDefault;
// When not using the default settings, this specifies one of the 4 reverb types
// 1 = Room 2 = Hall 3 = Plate 4 = Tap
- int RevType;
+ unsigned char RevType;
// This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
- int RevTime;
+ unsigned char RevTime;
// This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
- int RevLevel;
+ unsigned char RevLevel;
+ // 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
+ void (*report)(void *userData, ReportType type, 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
@@ -85,73 +104,98 @@ typedef void (*recalcStatusCallback)(int percDone);
// 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(int sampRate, recalcStatusCallback callBack);
+bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
extern iir_filter_type usefilter;
-extern partialFormat PCM[54];
-extern int16 romfile[262656];
-extern int32 divtable[256];
-extern int32 smalldivtable[256];
-extern uint32 wavtabler[64][256];
-extern uint32 looptabler[16][16][256];
-extern int16 sintable[65536];
-extern int32 penvtable[16][128];
-extern int32 pulsetable[101];
-extern int32 pulseoffset[101];
-extern int32 sawtable[128][128];
-extern float filtcoeff[FILTERGRAN][32][16];
-extern uint32 lfoptable[101][128];
-extern int32 ampveltable[128][64];
-extern int32 amptable[129];
-extern int16 smallnoise[441];
-extern int32 samplepos;
-extern int16* waveforms[4][256];
-extern uint32 waveformsize[4][256];
-extern int8 LoopPatterns[16][16];
-extern int drumPan[30][2];
-extern float ResonFactor[32];
-extern float ResonInv[32];
-
-extern int32 getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly);
-extern int32 getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly);
-extern int32 getFiltEnvelope(int16 wg, dpoly::partialStatus *pStat, dpoly *poly);
-
-class CSynthMT32 {
+class Synth {
+friend class Part;
+friend class Partial;
+friend class TableInitialiser;
private:
+ bool isEnabled;
+
+ sampleFormat PCM[54];
+ sampleTable PCMList[128];
+ Bit32u PCMReassign[54];
+ Bit32s PCMLoopTable[54];
+
+ Bit16s romfile[PCMSIZE + GRAN];
+ Bit8s chantable[32];
+
+ #if MONITORPARTIALS == 1
+ static Bit32s samplepos = 0;
+ #endif
+
+ MT32RAMFormat mt32ram, mt32default;
+
+ revmodel *reverbModel;
+
+ Bit16s mastervolume;
+
+ char curRevMode;
+ char curRevTime;
+ Bit32u curRevLevel;
unsigned char initmode;
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 InitTables();
+
+ bool loadPreset(const char *filename);
+ void initReverb(char newRevMode, char newRevTime);
+ 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, Bit8u *sysex, Bit32u len);
+
+ bool loadDrums(const char *filename);
+ bool loadPCMToROMMap(const char *filename);
+ bool loadROM(const char *filename);
+ void dumpDrums(const char *filename);
+ // Save the system state to a sysex file specified by filename
+ int dumpSysex(char *filename);
+
+protected:
+ void report(ReportType type, void *reportData);
+ File *openFile(const char *filename, File::OpenMode mode);
+ void closeFile(File *file);
+ void printDebug(const char *fmt, ...);
public:
- CSynthMT32() : isOpen(false) {};
+ Synth();
+ ~Synth();
- // Used to initialized the MT-32. The useProp parameter specifies
- // properties for the synthesizer, as outlined in the structure above.
- // Returns TRUE if initialization was sucessful, otherwise returns FALSE.
- bool ClassicOpen(SynthProperties useProp);
+ // 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);
+ void close(void);
// Sends a 4-byte MIDI message to the MT-32 for immediate playback
- void PlayMsg(uint32 msg);
+ void playMsg(Bit32u msg);
// Sends a string of Sysex commands to the MT-32 for immediate interpretation
- void PlaySysex(uint8 * sysex, uint32 len);
-
- // Save the system state to a sysex file specified by filename
- int DumpSysex(char *filename);
+ // The length is in bytes
+ void playSysex(Bit8u *sysex, Bit32u len);
+ void playSysexWithoutFraming(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 MT32_CallBack(uint8 * stream, uint32 len, int volume);
-
+ void render(Bit16s * stream, Bit32u len);
};
+}
+
#endif
diff --git a/backends/midi/mt32/tables.cpp b/backends/midi/mt32/tables.cpp
new file mode 100644
index 0000000000..b09b3c58bc
--- /dev/null
+++ b/backends/midi/mt32/tables.cpp
@@ -0,0 +1,727 @@
+/* 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"
+
+// Determines how the waveform cache file is handled (must be regenerated after sampling rate change)
+#define WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache
+//#define WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache
+//#define WAVECACHEMODE 2 // Ignore existing cache, generate and save cache
+//#define WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache
+
+// Constant tuning for now
+#define TUNING 440.0f
+
+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};
+
+const Bit8s LoopPatterns[9][10] = {
+ { 2, 3, 4, 5, 6, 7, -1, -1, -1, -1},
+ { 8, 9, 10, 11, 12, 13, 14, 15, 16, -1},
+ {17, 18, 19, 20, 21, -1, -1, -1, -1, -1},
+ {22, 23, 24, 25, 26, 27, 28, 29, -1, -1},
+ {30, 31, 32, 33, 34, 35, 36, 37, -1, -1},
+ {45, 46, 47, 48, 49, 50, 51, 52, 53, -1},
+ {15, 11, 12, 13, 14, 15, 16, -1, -1, -1},
+ {30, 35, 32, 33, 34, -1, -1, -1, -1, -1},
+ { 2, 3, -1, -1, -1, -1, -1, -1, -1, -1},
+};
+
+static const Bit32s LoopPatternTuning[9][10] = {
+ {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1, -1, -1},
+ {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1},
+ {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1, -1, -1, -1},
+ {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1},
+ {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1},
+ {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1},
+ {0x2590B, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1, -1},
+ {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1, -1, -1, -1},
+ {0x1294A, 0x1294A, -1, -1, -1, -1, -1, -1, -1, -1},
+};
+
+// 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;
+
+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];
+Bit32s bendtable[49];
+float ResonFactor[31];
+float ResonInv[31];
+
+// Per-note initialisation tables follow
+
+Bit16s freqtable[NUMNOTES];
+Bit32s fildeptable[5][NUMNOTES];
+Bit32s timekeytable[5][NUMNOTES];
+int filttable[2][NUMNOTES][201];
+int nfilttable[NUMNOTES][101][101];
+
+Bit32s divtable[NUMNOTES];
+Bit32s smalldivtable[NUMNOTES];
+Bit32u wavtabler[54][NUMNOTES];
+Bit32u looptabler[9][10][NUMNOTES];
+Bit32s sawtable[NUMNOTES][101];
+
+Bit16s *waveforms[4][NUMNOTES];
+Bit32u waveformsize[4][NUMNOTES];
+static Bit16s tmpforms[4][65536];
+
+
+// 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(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) / (pow(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);
+ }
+ for (lf = 0; lf <= 48; lf++)
+ bendtable[lf] = (int)((powf(2.0f, (((float)lf / 12.0f) - 2.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] = (int)(127.0 * pow((float)lf / 127.0, LN));
+ }
+ for (lf = 0; lf < MAX_SAMPLE_OUTPUT; lf++) {
+ 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[lf] = (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(int f, Bit32s div) {
+ for (int rsaw = 0; rsaw <= 100; rsaw++) {
+ float fsaw;
+ if (rsaw < 50)
+ fsaw = 50.0f;
+ else
+ fsaw = (float)rsaw;
+ int tmpdiv = div << 1;
+
+ //(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132
+ float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f;
+ sawtable[f][rsaw] = (int)(sawfact * (float)tmpdiv) >> 16;
+ //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div>>16, rsaw, sawtable[f][rsaw]);
+ }
+}
+
+static void initDep(int f) {
+ for (int dep = 0; dep < 5; dep++) {
+ if (dep == 0) {
+ fildeptable[dep][f] = 256;
+ timekeytable[dep][f] = 256;
+ } else {
+ float depfac = 3000.0f;
+ float ff, tempdep;
+ depfac = (float)depexp[dep];
+
+ ff = ((float)f - (float)MIDDLEC) / depfac;
+ tempdep = powf(2, ff) * 256.0f;
+ fildeptable[dep][f] = (int)tempdep;
+
+ ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC-(float)f)) * tkcatmult[dep]);
+ timekeytable[dep][f] = (int)(ff * 256.0f);
+ }
+ }
+ //synth->printDebug("F %d d1 %x d2 %x d3 %x d4 %x d5 %x", f, fildeptable[0][f],fildeptable[1][f],fildeptable[2][f],fildeptable[3][f],fildeptable[4][f]);
+}
+
+void TableInitialiser::initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div) {
+ double sd = (2.0*PI)/((((float)div/65536.0)) * 4.0);
+ waveformsize[0][f] = div >> 14;
+ waveformsize[1][f] = div >> 14;
+ waveformsize[2][f] = div >> 14;
+ waveformsize[3][f] = div >> 13;
+ for (int i = 0; i < 4; i++)
+ waveforms[i][f] = new Bit16s[waveformsize[i][f]];
+ if (reading) {
+ for (int i = 0; i < 4; i++) {
+ size_t len = waveformsize[i][f] * sizeof(Bit16s);
+ if (file->read(waveforms[i][f], len) != len) {
+ synth->printDebug("Error reading wave file cache!");
+ break;
+ }
+ }
+ } else {
+ float dumbfire;
+ double saw = 0.0f;
+ double sa = 0.0;
+ int fa = 0;
+ memset(tmpforms, 0,sizeof(tmpforms));
+
+ while (sa <= (2.0 * PI)) {
+ float sqp;
+
+ if (sa < PI) {
+ sqp = -1;
+ sqp = (float)(sqp + (0.25 * (sa/PI)));
+ } else {
+ sqp=1;
+ sqp = (float)(sqp - (0.25 * ((sa-PI)/PI)));
+ }
+
+ saw = 0;
+ for (int sinus = 1; sinus * freq < rate; sinus++) {
+ double fsinus = (double)sinus;
+ saw += (1 / fsinus) * sin(fsinus * sa);
+ }
+
+ dumbfire = (float)(sa / 2);
+
+ //This works pretty good
+ tmpforms[0][fa] = (Bit16s)(saw * -ampsize / 2);
+ tmpforms[1][fa] = (Bit16s)(saw * ampsize / 2);
+
+ tmpforms[2][fa] = (Bit16s)(cos(dumbfire) * -ampsize);
+ tmpforms[3][fa * 2] = (Bit16s)(cos(sa - PI) * -ampsize);
+ tmpforms[3][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - PI) * -ampsize);
+
+ fa++;
+ sa += sd;
+ }
+
+ //synth->printDebug("f num %d freq %f and fa %d, div>>13=%d", f, freq, fa, div>>13);
+
+ for (int i = 0; i < 4; i++)
+ memcpy(waveforms[i][f], tmpforms[i], waveformsize[i][f] * sizeof (Bit16s));
+
+ if (writing) {
+ for (int i = 0; i < 4; i++) {
+ int len = waveformsize[i][f] * sizeof(Bit16s);
+ if (!file->write(waveforms[i][f], len)) {
+ synth->printDebug("Error writing waveform cache file");
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void initFiltTable(int f, 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);
+ filttable[0][f][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN);
+ if (filttable[0][f][tr]>=((FILTERGRAN*15)/16))
+ filttable[0][f][tr] = ((FILTERGRAN*15)/16);
+
+ float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f;
+ filttable[1][f][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN);
+ if (filttable[1][f][tr]>=((FILTERGRAN*15)/16))
+ filttable[1][f][tr] = ((FILTERGRAN*15)/16);
+ }
+}
+
+static void initNFiltTable(int f, 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;
+
+ nfilttable[f][cf][tf] = (int)((freq * freqsum) / (rate / 2)*FILTERGRAN);
+ if (nfilttable[f][cf][tf] >= ((FILTERGRAN * 15) / 16))
+ nfilttable[f][cf][tf] = ((FILTERGRAN * 15) / 16);
+ }
+ }
+}
+
+void TableInitialiser::initNotes(Synth *synth, sampleFormat pcms[54], float rate) {
+ char filename[32];
+ int intRate = (int)rate;
+ sprintf(filename, "waveformcache-%d.raw", intRate);
+
+ File *file = NULL;
+ bool reading = false, writing = false;
+ char header[16];
+ strncpy(header, "MT32WAVE", 8);
+ int pos = 8;
+ // Version...
+ for (int i = 0; i < 4; i++)
+ header[pos++] = 0;
+ header[pos++] = (char)(intRate & 0xFF);
+ header[pos++] = (char)((intRate >> 8) & 0xFF);
+ header[pos++] = (char)((intRate >> 16) & 0xFF);
+ header[pos] = (char)((intRate >> 24) & 0xFF);
+#if WAVECACHEMODE < 2
+ file = synth->openFile(filename, File::OpenMode_read);
+ if (file != NULL) {
+ char fileHeader[16];
+ if (file->read(fileHeader, 16) == 16) {
+ if (memcmp(fileHeader, header, 16) == 0) {
+ reading = true;
+ } else {
+ synth->printDebug("Header of %s does not match expected - will generate", filename);
+ }
+ } else {
+ synth->printDebug("Error reading 16 bytes of %s - will generate", filename);
+ }
+ } else {
+ synth->printDebug("Unable to open %s for reading - will generate", filename);
+ }
+#endif
+#if WAVECACHEMODE == 0 || WAVECACHEMODE == 2
+ if (!reading) {
+ file = synth->openFile(filename, File::OpenMode_write);
+ if (file != NULL) {
+ if (file->write(header, 16) == 16) {
+ writing = true;
+ } 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
+
+ double ampsize = WGAMP;
+ for (int f = 12; f < 109; f++) {
+ float freq = (float)(TUNING * pow(2.0, ((double)f - 69.0) / 12.0));
+ freqtable[f] = (Bit16s)freq;
+ divtable[f] = (int)(rate / freq);
+ smalldivtable[f] = divtable[f] << 8;
+ divtable[f] = divtable[f] << 16;
+
+ initSaw(f, divtable[f]);
+ initDep(f);
+
+ //synth->printDebug("F %d sd %f div %d", f, sd, divtable[f]);
+ initWave(synth, file, reading, writing, f, freq, rate, ampsize, divtable[f]);
+
+ // Create the pitch tables
+
+ float tuner = (32000.0f / rate) * 65536.0f;
+ for (int pc = 0; pc < 54; pc++)
+ wavtabler[pc][f] = (int)(tuner * (freq / pcms[pc].tune));
+ for (int lp = 0; lp < 9; lp++) {
+ for (int ln = 0; ln < 10; ln++) {
+ // FIXME:KG: Surely this needs to be adjusted for the rate?
+ looptabler[lp][ln][f] = (int)((float)LoopPatternTuning[lp][ln] * (freq / 220.0f));
+ }
+ }
+
+ initFiltTable(f, freq, rate);
+ initNFiltTable(f, freq, rate);
+ }
+ if (file != NULL)
+ synth->closeFile(file);
+}
+
+void TableInitialiser::initMT32Tables(Synth *synth, sampleFormat pcms[54], float sampleRate) {
+ if (sampleRate <= 0.0f) {
+ synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);
+ }
+ if (initialisedSampleRate == 0.0f) {
+ initMT32ConstantTables(synth);
+ initFiltCoeff(sampleRate);
+ initEnvelopes(sampleRate);
+ initialisedSampleRate = sampleRate;
+ }
+ // This always needs to be done, to allocate the waveforms
+ initNotes(synth, pcms, sampleRate);
+}
+
+}
diff --git a/backends/midi/mt32/tables.h b/backends/midi/mt32/tables.h
new file mode 100644
index 0000000000..1f4fdab8b2
--- /dev/null
+++ b/backends/midi/mt32/tables.h
@@ -0,0 +1,97 @@
+/* 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
+
+// Mathematical constants
+#ifndef PI
+#define PI 3.1415926535897932384626433832795
+#endif
+#ifndef LN
+#define LN 2.30258509
+#endif
+
+// Filter settings
+#define FILTERGRAN 512
+
+#define MIDDLEC 60
+
+#define NUMNOTES 128 // MIDI supports 128 notes/keys
+
+// Amplitude of waveform generator
+#define WGAMP (7168)
+//#define WGAMP (8192)
+
+namespace MT32Emu {
+
+class Synth;
+
+extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT];
+
+// Some optimization stuff
+extern Bit32s keytable[217];
+extern Bit32s divtable[NUMNOTES];
+extern Bit32s smalldivtable[NUMNOTES];
+extern Bit32u wavtabler[54][NUMNOTES];
+extern Bit32u looptabler[9][10][NUMNOTES];
+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 sawtable[NUMNOTES][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];
+
+extern Bit16s freqtable[NUMNOTES];
+extern Bit32s fildeptable[5][NUMNOTES];
+extern Bit32s timekeytable[5][NUMNOTES];
+extern int filttable[2][NUMNOTES][201];
+extern int nfilttable[NUMNOTES][101][101];
+
+extern const Bit8s LoopPatterns[9][10];
+
+extern Bit16s *waveforms[4][NUMNOTES];
+extern Bit32u waveformsize[4][NUMNOTES];
+
+class TableInitialiser {
+ void initMT32ConstantTables(Synth *synth);
+ void initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div);
+ void initNotes(Synth *synth, sampleFormat pcms[54], float rate);
+public:
+ void initMT32Tables(Synth *synth, sampleFormat pcms[54], float sampleRate);
+};
+
+}
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 85e128b7dd..22dfd9c5d2 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -13,19 +13,14 @@ MODULE_OBJS := \
backends/midi/alsa.o \
backends/midi/windows.o \
backends/midi/ym2612.o \
- backends/midi/mt32/mt32.o \
- backends/midi/mt32/partial.o \
- backends/midi/mt32/synth.o \
- backends/midi/mt32/freeverb.o
-
+ backends/midi/mt32.o
MODULE_DIRS += \
backends \
backends/fs/posix \
backends/fs/morphos \
backends/fs/windows \
- backends/midi \
- backends/midi/mt32
+ backends/midi
# Include common rules
include $(srcdir)/common.rules
diff --git a/base/gameDetector.cpp b/base/gameDetector.cpp
index 708097d2fd..c35c32ea22 100644
--- a/base/gameDetector.cpp
+++ b/base/gameDetector.cpp
@@ -598,9 +598,9 @@ MidiDriver *GameDetector::createMidi(int midiDriver) {
// if need be, and we only have to specify a native
// driver.
case MD_ADLIB: return NULL;
-
- case MD_MT32: return MidiDriver_MT32_create(g_engine->_mixer, ConfMan.get("extrapath").c_str());
-
+#ifdef USE_MT32EMU
+ case MD_MT32: return MidiDriver_MT32_create(g_engine->_mixer);
+#endif
case MD_TOWNS: return MidiDriver_YM2612_create(g_engine->_mixer);
// Right now PC Speaker and PCjr are handled
diff --git a/configure b/configure
index b4b3939a34..06fa93352f 100755
--- a/configure
+++ b/configure
@@ -20,6 +20,7 @@ _mad=auto
_alsa=auto
_zlib=auto
_mpeg2=auto
+_mt32emu=yes
# default option behaviour yes/no
_build_scumm=yes
_build_simon=yes
@@ -269,6 +270,7 @@ Optional Features:
--enable-kyra build the Legend of Kyrandia engine
--enable-plugins build engines as loadable modules instead of
static linking them
+ --disable-mt32emu don't enable the integrated MT-32 emulator
Optional Libraries:
--with-alsa-prefix=DIR Prefix where alsa is installed (optional)
@@ -334,6 +336,8 @@ for ac_option in $@; do
--disable-nasm) _nasm=no ;;
--disable-mpeg2) _mpeg2=no ;;
--enable-plugins) _build_plugins=yes ;;
+ --enable-mt32emu) _mt32emu=yes ;;
+ --disable-mt32emu) _mt32emu=no ;;
--with-mpeg2-prefix=*)
_prefix=`echo $ac_option | cut -d '=' -f 2`
MPEG2_CFLAGS="-I$_prefix/include"
@@ -783,6 +787,19 @@ LIBS += -ldl
fi
echo "$_build_plugins"
+
+#
+# Check whether integrated MT-32 emulator support is requested
+#
+if test "$_mt32emu" = no ; then
+ _def_mt32emu='#undef USE_MT32EMU'
+ _mak_mt32emu='# USE_MT32EMU = 1'
+else
+ _def_mt32emu='#define USE_MT32EMU'
+ _mak_mt32emu='USE_MT32EMU = 1'
+ INCLUDES="$INCLUDES -I\$(srcdir)/backends/midi/mt32"
+fi
+
#
# Check for Ogg Vorbis
#
@@ -1058,8 +1075,9 @@ $_def_mad
$_def_alsa
$_def_zlib
$_def_mpeg2
+$_def_mt32emu
-/* Should we use i386 assembly routines */
+/* Whether we should use i386 assembly routines */
$_def_nasm
#endif /* CONFIG_H */
@@ -1106,6 +1124,7 @@ $_mak_sword2
$_mak_queen
$_mak_kyra
$_mak_saga
+$_mak_mt32emu
INCLUDES += $INCLUDES
OBJS += $OBJS
diff --git a/dists/msvc7/scummvm.vcproj b/dists/msvc7/scummvm.vcproj
index d74ac750ca..d0edb78add 100644
--- a/dists/msvc7/scummvm.vcproj
+++ b/dists/msvc7/scummvm.vcproj
@@ -461,6 +461,9 @@
RelativePath="..\..\backends\midi\adlib.cpp">
</File>
<File
+ RelativePath="..\..\backends\midi\mt32.cpp">
+ </File>
+ <File
RelativePath="..\..\backends\midi\null.cpp">
</File>
<File
@@ -472,13 +475,25 @@
<Filter
Name="mt32">
<File
- RelativePath="..\..\backends\midi\mt32\freeverb.cpp">
+ RelativePath="..\..\backends\midi\mt32\file.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\file.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\i386.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\i386.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\mt32emu.h">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\freeverb.h">
+ RelativePath="..\..\backends\midi\mt32\part.cpp">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\mt32.cpp">
+ RelativePath="..\..\backends\midi\mt32\part.h">
</File>
<File
RelativePath="..\..\backends\midi\mt32\partial.cpp">
@@ -487,6 +502,12 @@
RelativePath="..\..\backends\midi\mt32\partial.h">
</File>
<File
+ RelativePath="..\..\backends\midi\mt32\partialManager.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\partialManager.h">
+ </File>
+ <File
RelativePath="..\..\backends\midi\mt32\structures.h">
</File>
<File
@@ -495,6 +516,39 @@
<File
RelativePath="..\..\backends\midi\mt32\synth.h">
</File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\tables.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\tables.h">
+ </File>
+ <Filter
+ Name="freeverb">
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\allpass.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\allpass.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\comb.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\comb.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\denormals.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\revmodel.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\revmodel.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\tuning.h">
+ </File>
+ </Filter>
</Filter>
</Filter>
</Filter>
diff --git a/dists/msvc8/scummvm.vcproj b/dists/msvc8/scummvm.vcproj
index 7448d97488..888ec8f895 100644
--- a/dists/msvc8/scummvm.vcproj
+++ b/dists/msvc8/scummvm.vcproj
@@ -625,6 +625,10 @@
>
</File>
<File
+ RelativePath="..\..\backends\midi\mt32.cpp"
+ >
+ </File>
+ <File
RelativePath="..\..\backends\midi\null.cpp"
>
</File>
@@ -637,40 +641,82 @@
>
</File>
<Filter
- Name="mt32"
- >
+ Name="mt32">
<File
- RelativePath="..\..\backends\midi\mt32\freeverb.cpp"
- >
+ RelativePath="..\..\backends\midi\mt32\file.cpp">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\freeverb.h"
- >
+ RelativePath="..\..\backends\midi\mt32\file.h">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\mt32.cpp"
- >
+ RelativePath="..\..\backends\midi\mt32\i386.cpp">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\partial.cpp"
- >
+ RelativePath="..\..\backends\midi\mt32\i386.h">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\partial.h"
- >
+ RelativePath="..\..\backends\midi\mt32\mt32emu.h">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\structures.h"
- >
+ RelativePath="..\..\backends\midi\mt32\part.cpp">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\synth.cpp"
- >
+ RelativePath="..\..\backends\midi\mt32\part.h">
</File>
<File
- RelativePath="..\..\backends\midi\mt32\synth.h"
- >
+ RelativePath="..\..\backends\midi\mt32\partial.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\partial.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\partialManager.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\partialManager.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\structures.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\synth.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\synth.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\tables.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\tables.h">
</File>
+ <Filter
+ Name="freeverb">
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\allpass.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\allpass.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\comb.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\comb.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\denormals.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\revmodel.cpp">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\revmodel.h">
+ </File>
+ <File
+ RelativePath="..\..\backends\midi\mt32\freeverb\tuning.h">
+ </File>
+ </Filter>
</Filter>
</Filter>
</Filter>
diff --git a/scumm/scumm.cpp b/scumm/scumm.cpp
index 37d5f07648..9207f19431 100644
--- a/scumm/scumm.cpp
+++ b/scumm/scumm.cpp
@@ -1382,9 +1382,6 @@ void ScummEngine::setupMusic(int midi) {
}
if (midi == MDT_TOWNS)
_imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
- if (_midiDriver == MD_MT32) {
- _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
- }
_imuse->set_music_volume(ConfMan.getInt("music_volume"));
}
}
diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp
index 5c66297f59..ed37e93272 100644
--- a/sound/mididrv.cpp
+++ b/sound/mididrv.cpp
@@ -55,7 +55,9 @@ static const struct MidiDriverDescription midiDrivers[] = {
{"towns", "FM Towns", MD_TOWNS},
{"pcspk", "PC Speaker", MD_PCSPK},
{"pcjr", "IBM PCjr", MD_PCJR},
+#ifdef USE_MT32EMU
{"mt32", "MT-32", MD_MT32},
+#endif
#if defined(__PALM_OS__)
{"ypa1", "Yamaha Pa1", MD_YPA1},
diff --git a/sound/mididrv.h b/sound/mididrv.h
index 9300e47b3a..b6e57fe7f9 100644
--- a/sound/mididrv.h
+++ b/sound/mididrv.h
@@ -178,7 +178,9 @@ extern MidiDriver *MidiDriver_CORE_create();
extern MidiDriver *MidiDriver_ETUDE_create();
extern MidiDriver *MidiDriver_ALSA_create();
extern MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer);
-extern MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer, const char *path);
+#ifdef USE_MT32EMU
+extern MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer);
+#endif
extern MidiDriver *MidiDriver_YamahaPa1_create();
extern MidiDriver *MidiDriver_Zodiac_create();