aboutsummaryrefslogtreecommitdiff
path: root/backends/midi/mt32
diff options
context:
space:
mode:
authorEugene Sandulenko2004-10-21 22:37:37 +0000
committerEugene Sandulenko2004-10-21 22:37:37 +0000
commit9d0b746aaaaf78cfcc18e866e4375ad1de533bc4 (patch)
treef6570ed9ff19e0111704e638c6eec19e0f454f15 /backends/midi/mt32
parent9cb88f1d46388561457f44fb19dbe8d508ba14fc (diff)
downloadscummvm-rg350-9d0b746aaaaf78cfcc18e866e4375ad1de533bc4.tar.gz
scummvm-rg350-9d0b746aaaaf78cfcc18e866e4375ad1de533bc4.tar.bz2
scummvm-rg350-9d0b746aaaaf78cfcc18e866e4375ad1de533bc4.zip
Patch #1048326 Better MT-32 support
svn-id: r15635
Diffstat (limited to 'backends/midi/mt32')
-rw-r--r--backends/midi/mt32/.cvsignore1
-rw-r--r--backends/midi/mt32/freeverb.cpp356
-rw-r--r--backends/midi/mt32/freeverb.h246
-rw-r--r--backends/midi/mt32/mt32.cpp135
-rw-r--r--backends/midi/mt32/partial.cpp591
-rw-r--r--backends/midi/mt32/partial.h115
-rw-r--r--backends/midi/mt32/structures.h780
-rw-r--r--backends/midi/mt32/synth.cpp4564
-rw-r--r--backends/midi/mt32/synth.h170
9 files changed, 6958 insertions, 0 deletions
diff --git a/backends/midi/mt32/.cvsignore b/backends/midi/mt32/.cvsignore
new file mode 100644
index 0000000000..39a06683b7
--- /dev/null
+++ b/backends/midi/mt32/.cvsignore
@@ -0,0 +1 @@
+.deps
diff --git a/backends/midi/mt32/freeverb.cpp b/backends/midi/mt32/freeverb.cpp
new file mode 100644
index 0000000000..56fd5e4001
--- /dev/null
+++ b/backends/midi/mt32/freeverb.cpp
@@ -0,0 +1,356 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Copyright (C) 2000 Jezar at Dreampoint
+ *
+ * This code is public domain
+ *
+ * Parts of this code are:
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+// Comb filter implementation
+//
+// Written by
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#include "stdafx.h"
+#include "backends/midi/mt32/freeverb.h"
+
+comb::comb()
+{
+ filterstore = 0;
+ bufidx = 0;
+}
+
+void comb::setbuffer(float *buf, int size)
+{
+ buffer = buf;
+ bufsize = size;
+}
+
+void comb::mute()
+{
+ for (int i=0; i<bufsize; i++)
+ buffer[i]=0;
+}
+
+void comb::setdamp(float val)
+{
+ damp1 = val;
+ damp2 = 1-val;
+}
+
+float comb::getdamp()
+{
+ return damp1;
+}
+
+void comb::setfeedback(float val)
+{
+ feedback = val;
+}
+
+float comb::getfeedback()
+{
+ return feedback;
+}
+
+// Allpass filter implementation
+
+allpass::allpass()
+{
+ bufidx = 0;
+}
+
+void allpass::setbuffer(float *buf, int size)
+{
+ buffer = buf;
+ bufsize = size;
+}
+
+void allpass::mute()
+{
+ for (int i=0; i<bufsize; i++)
+ buffer[i]=0;
+}
+
+void allpass::setfeedback(float val)
+{
+ feedback = val;
+}
+
+float allpass::getfeedback()
+{
+ return feedback;
+}
+
+// Reverb model implementation
+
+revmodel::revmodel()
+{
+ // Tie the components to their buffers
+ combL[0].setbuffer(bufcombL1,combtuningL1);
+ combR[0].setbuffer(bufcombR1,combtuningR1);
+ combL[1].setbuffer(bufcombL2,combtuningL2);
+ combR[1].setbuffer(bufcombR2,combtuningR2);
+ combL[2].setbuffer(bufcombL3,combtuningL3);
+ combR[2].setbuffer(bufcombR3,combtuningR3);
+ combL[3].setbuffer(bufcombL4,combtuningL4);
+ combR[3].setbuffer(bufcombR4,combtuningR4);
+ combL[4].setbuffer(bufcombL5,combtuningL5);
+ combR[4].setbuffer(bufcombR5,combtuningR5);
+ combL[5].setbuffer(bufcombL6,combtuningL6);
+ combR[5].setbuffer(bufcombR6,combtuningR6);
+ combL[6].setbuffer(bufcombL7,combtuningL7);
+ combR[6].setbuffer(bufcombR7,combtuningR7);
+ combL[7].setbuffer(bufcombL8,combtuningL8);
+ combR[7].setbuffer(bufcombR8,combtuningR8);
+ allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
+ allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
+ allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
+ allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
+ allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
+ allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
+ allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
+ allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
+
+ // Set default values
+ allpassL[0].setfeedback(0.5f);
+ allpassR[0].setfeedback(0.5f);
+ allpassL[1].setfeedback(0.5f);
+ allpassR[1].setfeedback(0.5f);
+ allpassL[2].setfeedback(0.5f);
+ allpassR[2].setfeedback(0.5f);
+ allpassL[3].setfeedback(0.5f);
+ allpassR[3].setfeedback(0.5f);
+ setwet(initialwet);
+ setroomsize(initialroom);
+ setdry(initialdry);
+ setdamp(initialdamp);
+ setwidth(initialwidth);
+ setmode(initialmode);
+
+ // Buffer will be full of rubbish - so we MUST mute them
+ mute();
+}
+
+void revmodel::mute()
+{
+ int i;
+
+ if (getmode() >= freezemode)
+ return;
+
+ for (i=0;i<numcombs;i++)
+ {
+ combL[i].mute();
+ combR[i].mute();
+ }
+ for (i=0;i<numallpasses;i++)
+ {
+ allpassL[i].mute();
+ allpassR[i].mute();
+ }
+}
+
+void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip)
+{
+ float outL,outR,input;
+
+ while(numsamples-- > 0)
+ {
+ int i;
+
+ outL = outR = 0;
+ input = (*inputL + *inputR) * gain;
+
+ // Accumulate comb filters in parallel
+ for(i=0; i<numcombs; i++)
+ {
+ outL += combL[i].process(input);
+ outR += combR[i].process(input);
+ }
+
+ // Feed through allpasses in series
+ for(i=0; i<numallpasses; i++)
+ {
+ outL = allpassL[i].process(outL);
+ outR = allpassR[i].process(outR);
+ }
+
+ // Calculate output REPLACING anything already there
+ *outputL = outL*wet1 + outR*wet2 + *inputL*dry;
+ *outputR = outR*wet1 + outL*wet2 + *inputR*dry;
+
+ // Increment sample pointers, allowing for interleave (if any)
+ inputL += skip;
+ inputR += skip;
+ outputL += skip;
+ outputR += skip;
+ }
+}
+
+void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip)
+{
+ float outL,outR,input;
+
+ while(numsamples-- > 0)
+ {
+ int i;
+
+ outL = outR = 0;
+ input = (*inputL + *inputR) * gain;
+
+ // Accumulate comb filters in parallel
+ for(i=0; i<numcombs; i++)
+ {
+ outL += combL[i].process(input);
+ outR += combR[i].process(input);
+ }
+
+ // Feed through allpasses in series
+ for(i=0; i<numallpasses; i++)
+ {
+ outL = allpassL[i].process(outL);
+ outR = allpassR[i].process(outR);
+ }
+
+ // Calculate output MIXING with anything already there
+ *outputL += outL*wet1 + outR*wet2 + *inputL*dry;
+ *outputR += outR*wet1 + outL*wet2 + *inputR*dry;
+
+ // Increment sample pointers, allowing for interleave (if any)
+ inputL += skip;
+ inputR += skip;
+ outputL += skip;
+ outputR += skip;
+ }
+}
+
+void revmodel::update()
+{
+// Recalculate internal values after parameter change
+
+ int i;
+
+ wet1 = wet*(width/2 + 0.5f);
+ wet2 = wet*((1-width)/2);
+
+ if (mode >= freezemode)
+ {
+ roomsize1 = 1;
+ damp1 = 0;
+ gain = muted;
+ }
+ else
+ {
+ roomsize1 = roomsize;
+ damp1 = damp;
+ gain = fixedgain;
+ }
+
+ for(i=0; i<numcombs; i++)
+ {
+ combL[i].setfeedback(roomsize1);
+ combR[i].setfeedback(roomsize1);
+ }
+
+ for(i=0; i<numcombs; i++)
+ {
+ combL[i].setdamp(damp1);
+ combR[i].setdamp(damp1);
+ }
+}
+
+// The following get/set functions are not inlined, because
+// speed is never an issue when calling them, and also
+// because as you develop the reverb model, you may
+// wish to take dynamic action when they are called.
+
+void revmodel::setroomsize(float value)
+{
+ roomsize = (value*scaleroom) + offsetroom;
+ update();
+}
+
+float revmodel::getroomsize()
+{
+ return (roomsize-offsetroom)/scaleroom;
+}
+
+void revmodel::setdamp(float value)
+{
+ damp = value*scaledamp;
+ update();
+}
+
+float revmodel::getdamp()
+{
+ return damp/scaledamp;
+}
+
+void revmodel::setwet(float value)
+{
+ wet = value*scalewet;
+ update();
+}
+
+float revmodel::getwet()
+{
+ return wet/scalewet;
+}
+
+void revmodel::setdry(float value)
+{
+ dry = value*scaledry;
+}
+
+float revmodel::getdry()
+{
+ return dry/scaledry;
+}
+
+void revmodel::setwidth(float value)
+{
+ width = value;
+ update();
+}
+
+float revmodel::getwidth()
+{
+ return width;
+}
+
+void revmodel::setmode(float value)
+{
+ mode = value;
+ update();
+}
+
+float revmodel::getmode()
+{
+ if (mode >= freezemode)
+ return 1;
+ else
+ return 0;
+}
+
+//ends
+
diff --git a/backends/midi/mt32/freeverb.h b/backends/midi/mt32/freeverb.h
new file mode 100644
index 0000000000..b0d32c8f3f
--- /dev/null
+++ b/backends/midi/mt32/freeverb.h
@@ -0,0 +1,246 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Copyright (C) 2000 Jezar at Dreampoint
+ *
+ * This code is public domain
+ *
+ * Parts of this code are:
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+// Macro for killing denormalled numbers
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// Based on IS_DENORMAL macro by Jon Watte
+// This code is public domain
+
+#ifndef _freeverb_h_
+#define _freeverb_h_
+
+#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
+
+// Comb filter class declaration
+
+class comb
+{
+public:
+ comb();
+ void setbuffer(float *buf, int size);
+ inline float process(float inp);
+ void mute();
+ void setdamp(float val);
+ float getdamp();
+ void setfeedback(float val);
+ float getfeedback();
+private:
+ float feedback;
+ float filterstore;
+ float damp1;
+ float damp2;
+ float *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float comb::process(float input)
+{
+ float output;
+
+ output = buffer[bufidx];
+ undenormalise(output);
+
+ filterstore = (output*damp2) + (filterstore*damp1);
+ undenormalise(filterstore);
+
+ buffer[bufidx] = input + (filterstore*feedback);
+
+ if(++bufidx>=bufsize) bufidx = 0;
+
+ return output;
+}
+
+// Allpass filter declaration
+
+class allpass
+{
+public:
+ allpass();
+ void setbuffer(float *buf, int size);
+ inline float process(float inp);
+ void mute();
+ void setfeedback(float val);
+ float getfeedback();
+// private:
+ float feedback;
+ float *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float allpass::process(float input)
+{
+ float output;
+ float bufout;
+
+ bufout = buffer[bufidx];
+ undenormalise(bufout);
+
+ output = -input + bufout;
+ buffer[bufidx] = input + (bufout*feedback);
+
+ if(++bufidx>=bufsize) bufidx = 0;
+
+ return output;
+}
+
+
+// Reverb model tuning values
+
+const int numcombs = 8;
+const int numallpasses = 4;
+const float muted = 0;
+const float fixedgain = 0.015f;
+const float scalewet = 3;
+const float scaledry = 2;
+const float scaledamp = 0.4f;
+const float scaleroom = 0.28f;
+const float offsetroom = 0.7f;
+const float initialroom = 0.5f;
+const float initialdamp = 0.5f;
+const float initialwet = 1/scalewet;
+const float initialdry = 0;
+const float initialwidth = 1;
+const float initialmode = 0;
+const float freezemode = 0.5f;
+const int stereospread = 23;
+
+// These values assume 44.1KHz sample rate
+// they will probably be OK for 48KHz sample rate
+// but would need scaling for 96KHz (or other) sample rates.
+// The values were obtained by listening tests.
+const int combtuningL1 = 1116;
+const int combtuningR1 = 1116+stereospread;
+const int combtuningL2 = 1188;
+const int combtuningR2 = 1188+stereospread;
+const int combtuningL3 = 1277;
+const int combtuningR3 = 1277+stereospread;
+const int combtuningL4 = 1356;
+const int combtuningR4 = 1356+stereospread;
+const int combtuningL5 = 1422;
+const int combtuningR5 = 1422+stereospread;
+const int combtuningL6 = 1491;
+const int combtuningR6 = 1491+stereospread;
+const int combtuningL7 = 1557;
+const int combtuningR7 = 1557+stereospread;
+const int combtuningL8 = 1617;
+const int combtuningR8 = 1617+stereospread;
+const int allpasstuningL1 = 556;
+const int allpasstuningR1 = 556+stereospread;
+const int allpasstuningL2 = 441;
+const int allpasstuningR2 = 441+stereospread;
+const int allpasstuningL3 = 341;
+const int allpasstuningR3 = 341+stereospread;
+const int allpasstuningL4 = 225;
+const int allpasstuningR4 = 225+stereospread;
+
+
+// Reverb model declaration
+
+class revmodel
+{
+public:
+ revmodel();
+ void mute();
+ void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
+ void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
+ void setroomsize(float value);
+ float getroomsize();
+ void setdamp(float value);
+ float getdamp();
+ void setwet(float value);
+ float getwet();
+ void setdry(float value);
+ float getdry();
+ void setwidth(float value);
+ float getwidth();
+ void setmode(float value);
+ float getmode();
+private:
+ void update();
+private:
+ float gain;
+ float roomsize,roomsize1;
+ float damp,damp1;
+ float wet,wet1,wet2;
+ float dry;
+ float width;
+ float mode;
+
+ // The following are all declared inline
+ // to remove the need for dynamic allocation
+ // with its subsequent error-checking messiness
+
+ // Comb filters
+ comb combL[numcombs];
+ comb combR[numcombs];
+
+ // Allpass filters
+ allpass allpassL[numallpasses];
+ allpass allpassR[numallpasses];
+
+ // Buffers for the combs
+ float bufcombL1[combtuningL1];
+ float bufcombR1[combtuningR1];
+ float bufcombL2[combtuningL2];
+ float bufcombR2[combtuningR2];
+ float bufcombL3[combtuningL3];
+ float bufcombR3[combtuningR3];
+ float bufcombL4[combtuningL4];
+ float bufcombR4[combtuningR4];
+ float bufcombL5[combtuningL5];
+ float bufcombR5[combtuningR5];
+ float bufcombL6[combtuningL6];
+ float bufcombR6[combtuningR6];
+ float bufcombL7[combtuningL7];
+ float bufcombR7[combtuningR7];
+ float bufcombL8[combtuningL8];
+ float bufcombR8[combtuningR8];
+
+ // Buffers for the allpasses
+ float bufallpassL1[allpasstuningL1];
+ float bufallpassR1[allpasstuningR1];
+ float bufallpassL2[allpasstuningL2];
+ float bufallpassR2[allpasstuningR2];
+ float bufallpassL3[allpasstuningL3];
+ float bufallpassR3[allpasstuningR3];
+ float bufallpassL4[allpasstuningL4];
+ float bufallpassR4[allpasstuningR4];
+};
+
+#endif//_freeverb_h_
+
+//ends
+
diff --git a/backends/midi/mt32/mt32.cpp b/backends/midi/mt32/mt32.cpp
new file mode 100644
index 0000000000..cb32b6e5e9
--- /dev/null
+++ b/backends/midi/mt32/mt32.cpp
@@ -0,0 +1,135 @@
+/* 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;
+
+ const char *rom_path;
+
+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 32000; }
+};
+
+
+////////////////////////////////////////
+//
+// MidiDriver_MT32
+//
+////////////////////////////////////////
+
+
+MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer, const char *path)
+ : MidiDriver_Emulated(mixer) {
+ _synth = new CSynthMT32();
+ rom_path = path;
+ File::addDefaultDirectory(path);
+}
+
+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(rom_path, 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((Bit8u *)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/partial.cpp b/backends/midi/mt32/partial.cpp
new file mode 100644
index 0000000000..363752ea7e
--- /dev/null
+++ b/backends/midi/mt32/partial.cpp
@@ -0,0 +1,591 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Based on Tristan's conversion of Canadacow's code
+ *
+ * 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$
+ *
+ */
+
+// Implementation of the MT-32 partial class
+
+#include "backends/midi/mt32/synth.h"
+#include "backends/midi/mt32/partial.h"
+
+INLINE void CPartialMT32::generateSamples(Bit16s * partialBuf, long length) {
+ if (!isActive) return;
+ if (alreadyOutputed) return;
+
+
+ alreadyOutputed = true;
+
+ // Generate samples
+
+ int r;
+ int i;
+ Bit32s envval, ampval, filtval;
+ soundaddr *pOff = &partCache->partialOff;
+ int noteval = partCache->keyedval;
+ for(i=0;i<length;i++) {
+ Bit32s ptemp = 0;
+
+ 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;
+ }
+ --partCache->envs[AMPENV].count;
+ }
+
+ int delta = 0x10707;
+
+ // Calculate Pitch envelope
+ int lfoat = 0x1000;
+ int pdep;
+ if(partCache->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;
+
+ int lfoatr = sintable[lfoatm];
+
+ lfoat = lfoptable[tcache->lfodepth][lfoatr];
+ }
+ pdep = partCache->pitchEnvCache;
+
+
+ } else {
+ envval = getPitchEnvelope(partCache,tmppoly);
+ int pd=tcache->pitchEnv.depth;
+ pdep = penvtable[pd][envval];
+ if(partCache->pitchsustain) partCache->pitchEnvCache = pdep;
+
+ }
+
+
+ // Get waveform - either PCM or synthesized sawtooth or square
+
+
+ if (tcache->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 * (Bit32s)(pOff->pcmoffs.pcmoffset>>8)) >>8));
+
+ } 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;
+
+
+
+ }
+ } 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 {
+ partCache->looppos++;
+ if(LoopPatterns[tPCM->aggSound][partCache->looppos]==-1) partCache->looppos=0;
+ pOff->pcmabs = 0;
+ }
+ //LOG_MSG("tPCM %d loops %d done %d playPart %d", tPCM->pcmnum, tPCM->loop, partCache->PCMDone, partCache->playPartial);
+
+ }
+
+
+ }
+
+ } else {
+ // Synthesis partial
+ int divis, hdivis, ofs, ofs3, toff;
+ int minorplace;
+
+ int wf = tcache->waveform ;
+
+ divis = divtable[noteval]>>15;
+
+ if(pOff->pcmoffs.pcmplace>=divis) pOff->pcmoffs.pcmplace = (Bit16u)(pOff->pcmoffs.pcmplace-divis);
+
+ toff = pOff->pcmoffs.pcmplace;
+ minorplace = pOff->pcmoffs.pcmoffset >> 14;
+
+ int pa, pb;
+
+ if(ampval>0) {
+
+ filtval = getFiltEnvelope((Bit16s)ptemp,partCache,tmppoly);
+
+ //LOG_MSG("Filtval: %d", filtval);
+
+ 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);
+ 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;
+
+ // Non-bandlimited squarewave
+ /*
+ ofs = (divis*pulsetable[tcache->pulsewidth])>>8;
+ if(toff < ofs) {
+ ptemp = 1 * WGAMP;
+ } 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
+ int 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;
+
+ // ptemp = (int)(sin((double)toff / 100.0) * 100.0);
+ //ptemp = pa;
+
+ // 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;
+
+
+ ofs = toff % (hdivis);
+
+ ofs3 = toff + ((divmark*pulsetable[tcache->pulsewidth])>>16);
+ ofs3 = ofs3 % (hdivis);
+
+ 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 = (Bit32s)(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;
+
+
+ }
+
+ // 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;
+ */
+
+ // Fix delta code
+ __int64 tdelta = (__int64)delta;
+ tdelta = (tdelta * tcache->fineshift)>>12;
+ tdelta = (tdelta * pdep)>>12;
+ tdelta = (tdelta * lfoat)>>12;
+ if(tcache->useBender) tdelta = (tdelta * *tmppoly->bendptr)>>12;
+
+ // Add calculated delta to our waveform offset
+ pOff->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++ = (Bit16s)ptemp;
+ }
+
+
+}
+
+INLINE void CPartialMT32::mixBuffers(Bit16s * buf1, Bit16s *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++) {
+ Bit32s tmp1 = buf1[i];
+ Bit32s tmp2 = buf2[i];
+ tmp1 += tmp2;
+ buf1[i] = (Bit16s)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
+#endif
+}
+
+INLINE void CPartialMT32::mixBuffersRingMix(Bit16s * buf1, Bit16s *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 = (a * b) + a;
+ if(a>1.0) a = 1.0;
+ if(a<-1.0) a = -1.0;
+ buf1[i] = (Bit16s)(a * 8192.0);
+
+ //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)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
+ }
+#else
+ atti386_mixBuffersRingMix(buf1, buf2, len);
+#endif
+#endif
+}
+
+INLINE void CPartialMT32::mixBuffersRing(Bit16s * buf1, Bit16s *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] = (Bit16s)(a * 8192.0);
+ //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10);
+ }
+#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
+ }
+#else
+ atti386_mixBuffersRing(buf1, buf2, len);
+#endif
+#endif
+}
+
+INLINE void CPartialMT32::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) {
+ int i,m;
+ m=0;
+ for(i=0;i<len;i++) {
+ *outBuf++ = (*buf1);
+ buf1++;
+ *outBuf++ = (*buf2);
+ buf2++;
+ }
+
+}
+
+bool CPartialMT32::produceOutput(Bit16s * partialBuf, long length) {
+ if (!isActive) return false;
+ if (alreadyOutputed) return false;
+ int i;
+
+ //alreadyOutputed = true;
+
+ memset(&pairBuffer[0],0,length*4);
+ memset(&myBuffer[0],0,length*4);
+ // 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;
+ }
+ }
+
+ generateSamples(myBuffer, length);
+/* FILE *fo = fopen("/tmp/samp.raw", "a");
+ for(i = 0; i < length; i++)
+ fwrite(myBuffer + i, 1, 2, fo);
+ fclose(fo);
+*/
+ Bit16s * p1buf, * p2buf;
+
+ if((partNum==0) || ((partNum==1) && (tibrePair==NULL))) {
+ p1buf = &myBuffer[0];
+ p2buf = &pairBuffer[0];
+ } else {
+ p2buf = &myBuffer[0];
+ p1buf = &pairBuffer[0];
+ }
+
+// LOG_MSG("useMix: %d", useMix);
+
+ switch(useMix) {
+ case 0:
+ // Standard sound mix
+ mixBuffers(p1buf, p2buf, length);
+ break;
+ case 1:
+ // Ring modulation with sound mix
+ mixBuffersRingMix(p1buf, p2buf, length);
+ break;
+ case 2:
+ // Ring modulation alone
+ mixBuffersRing(p1buf, p2buf, length);
+ break;
+ case 3:
+ // Stereo mixing. One partial to one channel, one to another.
+ mixBuffersStereo(p1buf, p2buf, partialBuf, length);
+ return true;
+ break;
+ default:
+ mixBuffers(p1buf, p2buf, length);
+ break;
+ }
+
+
+ int m;
+ m = 0;
+ Bit16s leftvol, rightvol;
+ if (!tmppoly->isRy) {
+ leftvol = tmppoly->pansetptr->leftvol;
+ rightvol = tmppoly->pansetptr->rightvol;
+ } else {
+ leftvol = (Bit16s)drumPan[tmppoly->pcmnum][0];
+ rightvol = (Bit16s)drumPan[tmppoly->pcmnum][1];
+ }
+
+#if USE_MMX == 0
+ for(i=0;i<length;i++) {
+ partialBuf[m] = (Bit16s)(((Bit32s)p1buf[i] * (Bit32s)leftvol) >> 16);
+ m++;
+ partialBuf[m] = (Bit16s)(((Bit32s)p1buf[i] * (Bit32s)rightvol) >> 16);
+ m++;
+ }
+#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
+ }
+#else
+ atti386_PartProductOutput(quadlen, leftvol, rightvol, partialBuf, p1buf);
+#endif
+#endif
+
+ return true;
+}
diff --git a/backends/midi/mt32/partial.h b/backends/midi/mt32/partial.h
new file mode 100644
index 0000000000..769284abf0
--- /dev/null
+++ b/backends/midi/mt32/partial.h
@@ -0,0 +1,115 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Based on Tristan's conversion of Canadacow's code
+ *
+ * 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$
+ *
+ */
+
+#ifndef __CPARTIALMT32_H__
+#define __CPARTIALMT32_H__
+
+#include "backends/midi/mt32/structures.h"
+
+// Class definition of MT-32 partials. 32 in all.
+class CPartialMT32 {
+private:
+ int useMix;
+ int partNum;
+
+ int pN;
+
+
+
+
+ Bit16s myBuffer[2048];
+ // For temporary output of paired buffer
+ Bit16s pairBuffer[2048];
+
+ void mixBuffers(Bit16s * buf1, Bit16s * buf2, int len);
+ void mixBuffersRingMix(Bit16s * buf1, Bit16s * buf2, int len);
+ void mixBuffersRing(Bit16s * buf1, Bit16s * buf2, int len);
+ void mixBuffersStereo(Bit16s * buf1, Bit16s * buf2, Bit16s * outBuf, int len);
+
+
+public:
+ patchCache *tcache;
+ patchCache cachebackup[4];
+
+ //FILE *fp;
+ //FILE *fp2;
+
+ dpoly::partialStatus *partCache;
+
+ CPartialMT32 *tibrePair;
+ bool isActive;
+ bool alreadyOutputed;
+ int ownerChan;
+ Bit64s 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; }
+
+
+ // Returns true only if data written to buffer
+ // This function (unline the one below it) returns processed stereo samples
+ // made from combining this single partial with its pair, if it has one.
+ bool produceOutput(Bit16s * partialBuf, long length);
+
+ // This function produces mono sample output of the specific partial
+ void generateSamples(Bit16s * partialBuf, long length);
+
+};
+
+
+#endif
+
diff --git a/backends/midi/mt32/structures.h b/backends/midi/mt32/structures.h
new file mode 100644
index 0000000000..7caf10cec0
--- /dev/null
+++ b/backends/midi/mt32/structures.h
@@ -0,0 +1,780 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Based on Tristan's conversion of Canadacow's code
+ *
+ * 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$
+ *
+ */
+
+#if !defined __MT32STRUCTURES_H__
+#define __MT32STRUCTURES_H__
+
+#include "stdafx.h"
+#include "common/scummsys.h"
+
+#if defined(_MSC_VER)
+typedef unsigned __int64 Bit64u;
+typedef signed __int64 Bit64s;
+#else
+typedef unsigned long long Bit64u;
+typedef signed long long Bit64s;
+#endif
+typedef unsigned int Bit32u;
+typedef signed int Bit32s;
+typedef unsigned short int Bit16u;
+typedef signed short int Bit16s;
+typedef unsigned char Bit8u;
+typedef signed char Bit8s;
+
+// The occurences of __int64 should be changed to Bit64s
+#define __int64 Bit64u
+
+#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);
+}
+
+#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW__))
+
+#define ALIGN_PACKED
+
+#else
+
+//#define ALIGN_PACKED __attribute__ ((__packed__))
+#define ALIGN_PACKED __attribute__ ((aligned (1)))
+
+#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, 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");
+}
+
+// FIXME: This is buggy
+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_PartProductOutput(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
+
+#endif
+
+extern bool enabled3DNow;
+extern bool enabledSSE;
+
+#pragma pack(1)
+struct timbreParam {
+ struct commonParam {
+ char name[10];
+ char pstruct12; // 1&2 0-12 (1-13)
+ char pstruct34; // #3&4 0-12 (1-13)
+ char pmute; // 0-15 (0000-1111)
+ char nosustain; // 0-1(Normal, No sustain)
+ } ALIGN_PACKED common;
+
+ struct partialParam {
+ struct wgParam {
+ char coarse; // 0-96 (C1,C#1-C9)
+ char fine; // 0-100 (-50 - +50)
+ char keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
+ char bender; // 0,1 (ON/OFF)
+ char waveform; // 0-1 (SQU/SAW)
+ char pcmwave; // 0-127 (1-128)
+ char pulsewid; // 0-100
+ char pwvelo; // 0-14 (-7 - +7)
+ } ALIGN_PACKED wg;
+
+ struct envParam {
+ char depth; // 0-10
+ char sensitivity; // 1-100
+ char timekeyfollow; // 0-4
+ 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
+ char modsense; // 0-100
+ } ALIGN_PACKED lfo;
+
+ struct tvfParam {
+ char cutoff; // 0-100
+ char resonance; // 0-30
+ char keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
+ char biaspoint; // 0-127 (<1A-<7C >1A-7C)
+ char biaslevel; // 0-14 (-7 - +7)
+ char envdepth; // 0-100
+ char envsense; // 0-100
+ char envdkf; // DEPTH KEY FOLL0W 0-4
+ char envtkf; // TIME KEY FOLLOW 0-4
+ char envtime[5]; // 1-100
+ char envlevel[4]; // 1-100
+ } ALIGN_PACKED tvf;
+
+ struct tvaParam {
+ char level; // 0-100
+ char velosens; // 0-100
+ char biaspoint1; // 0-127 (<1A-<7C >1A-7C)
+ char biaslevel1; // 0-12 (-12 - 0)
+ char biaspoint2; // 0-127 (<1A-<7C >1A-7C)
+ char biaslevel2; // 0-12 (-12 - 0)
+ char envtkf; // TIME KEY FOLLOW 0-4
+ char envvkf; // VELOS KEY FOLL0W 0-4
+ 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)
+ char outlevel; // OUTPUT LEVEL 0-100
+ char panpot; // PANPOT 0-14 (R-L)
+ char dummyv[6];
+ } ALIGN_PACKED tmpSettings[8];
+ struct ryhTemp {
+ 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];
+
+ timbreParam timTemp[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 {
+ 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)
+ char reverbLevel; // REVERB LEVEL 0-7 (1-8)
+ char reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
+ 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)];
+} ALIGN_PACKED;
+
+struct memAbsolute {
+ char mt32memory[sizeof(memBanks)];
+} ALIGN_PACKED;
+
+#pragma pack()
+
+struct partialFormat {
+ Bit32u addr;
+ Bit16u len;
+ bool loop;
+ float tune;
+ Bit32s ampval;
+};
+
+struct partialTable {
+ Bit32u addr;
+ Bit32u len;
+ Bit32u pcmnum;
+ Bit32s ampval;
+ bool loop;
+ Bit32s aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations
+};
+
+
+
+union soundaddr {
+ Bit32u pcmabs;
+ struct offsets {
+#if defined(SCUMM_LITTLE_ENDIAN)
+ Bit16u pcmoffset;
+ Bit16u pcmplace;
+#else
+ Bit16u pcmplace;
+ Bit16u pcmoffset;
+#endif
+ } pcmoffs;
+};
+
+
+struct volset {
+ Bit16s leftvol;
+ Bit16s rightvol;
+ Bit16s leftvol2;
+ Bit16s rightvol2;
+};
+
+struct patchCache {
+ int rawPCM;
+ partialTable convPCM;
+
+ bool playPartial;
+ bool usePartial;
+ bool PCMPartial;
+ char waveform;
+ int pulsewidth;
+ int pwsens;
+ int pitchshift;
+ int fineshift;
+ bool sustain;
+
+ int lfodepth;
+ int lforate;
+ Bit32u lfoperiod;
+ int modsense;
+
+ int keydir;
+ int pitchkeyfollow;
+ int pitchkeydir;
+
+ int filtkeyfollow;
+
+ int tvfbias;
+ int tvfblevel;
+ int tvfdir;
+
+ int ampbias[2];
+ int ampblevel[2];
+ int ampdir[2];
+
+ int ampdepth;
+ int ampenvdir;
+ int amplevel;
+ int tvfdepth;
+
+ int prevsample;
+
+ bool useBender;
+
+ timbreParam::partialParam::envParam pitchEnv;
+ timbreParam::partialParam::tvaParam ampEnv;
+ timbreParam::partialParam::tvfParam filtEnv;
+
+ Bit32s ampsustain;
+ Bit32s pitchsustain;
+ Bit32s filtsustain;
+
+ Bit32u partCount;
+
+ Bit8u padding[64]; //Used to pad the patch cache to 4096 bytes. This replaces an imul with a shl 12
+
+};
+
+struct dpoly {
+ bool isPlaying;
+ bool isDecay;
+ bool isActive;
+
+ bool partActive[4];
+
+ bool isRy;
+ Bit32u *bendptr;
+ Bit32u drumbend;
+ Bit32s *volumeptr;
+ volset *pansetptr;
+
+ int pcmnum;
+ int freq;
+ int freqnum;
+ int vel;
+
+ Bit32u partCount;
+
+ soundaddr pcmoff;
+ Bit32u 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 {
+ Bit32s envpos;
+ Bit32s envstat;
+ Bit32s envbase;
+ Bit32s envdist;
+ Bit32s envsize;
+
+ bool sustaining;
+ bool decaying;
+ bool notdecayed;
+ Bit32u decay;
+ Bit32s prevlevel;
+
+ Bit32s counter;
+ Bit32s count;
+
+ } envs[4];
+
+ Bit32u lfopos;
+ soundaddr partialOff;
+ soundaddr wgOff;
+
+ Bit32u ampEnvCache;
+ Bit32u 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;
+
+ int origpat;
+ int drumnum;
+
+ int age;
+
+ bool pedalhold;
+ bool firstsamp;
+
+ Bit32u P1Mix;
+ Bit32u P2Mix;
+ bool sustain;
+};
+
+#endif
diff --git a/backends/midi/mt32/synth.cpp b/backends/midi/mt32/synth.cpp
new file mode 100644
index 0000000000..9a35c7e5cb
--- /dev/null
+++ b/backends/midi/mt32/synth.cpp
@@ -0,0 +1,4564 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Based on Tristan's conversion of Canadacow's code
+ *
+ * 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 "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
+
+#pragma pack(1)
+static union mt32ramFormat {
+ memParams params;
+ memBanks patchabs;
+ memAbsolute memabs;
+
+
+ // System memory 10
+
+ // Display 20
+
+ // Reset 7F
+
+
+} ALIGN_PACKED mt32ram, mt32default;
+#pragma pack()
+// Borrowed from Borland's site
+int axtoi(char *hexStg) {
+ unsigned int n = 0; // position in string
+ unsigned int m = 0; // position in digit[] to shift
+ unsigned int count; // loop index
+ int intValue = 0; // integer value of hex string
+ int digit[512]; // hold values to convert
+ while (n < strlen(hexStg)) {
+ if (hexStg[n]=='\0')
+ break;
+ if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
+ digit[n] = hexStg[n] & 0x0f; //convert to int
+ else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else break;
+ n++;
+ }
+ count = n;
+ m = n - 1;
+ n = 0;
+ while(n < count) {
+ // digit[n] is value of hex digit at position n
+ // (m << 2) is the number of positions to shift
+ // OR the bits into return value
+ intValue = intValue | (digit[n] << (m << 2));
+ m--; // adjust the position to set
+ n++; // next digit to process
+ }
+ return (intValue);
+}
+
+
+typedef struct {
+ unsigned int length; /* size of filter */
+ float *history; /* pointer to history in filter */
+ float *coef; /* pointer to coefficients of filter */
+} FILTER;
+
+
+#define FILTER_SECTIONS 2 /* 2 filter sections for 24 db/oct filter */
+
+typedef struct {
+ double a0, a1, a2; /* numerator coefficients */
+ double b0, b1, b2; /* denominator coefficients */
+} BIQUAD;
+
+BIQUAD ProtoCoef[FILTER_SECTIONS]; /* Filter prototype coefficients,
+ 1 for each filter section */
+
+void prewarp(double *a0, double *a1, double *a2, double fc, double fs);
+void bilinear(
+ double a0, double a1, double a2, /* numerator coefficients */
+ double b0, double b1, double b2, /* denominator coefficients */
+ double *k, /* overall gain factor */
+ double fs, /* sampling rate */
+ float *coef); /* pointer to 4 iir coefficients */
+
+
+/*
+ * ----------------------------------------------------------
+ * 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, pi;
+
+ pi = 4.0 * atan(1.0);
+ wp = 2.0 * fs * tan(pi * fc / fs);
+
+ *a2 = (*a2) / (wp * wp);
+ *a1 = (*a1) / wp;
+}
+
+
+/*
+ * ----------------------------------------------------------
+ * bilinear()
+ *
+ * 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
+ * ----------------------------------------------------------
+ */
+void bilinear(
+ double a0, double a1, double a2, /* numerator coefficients */
+ double b0, double b1, double b2, /* denominator coefficients */
+ double *k, /* overall gain factor */
+ double fs, /* sampling rate */
+ float *coef /* pointer to 4 iir coefficients */
+)
+{
+ 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 */
+}
+
+void szxform(
+ double *a0, double *a1, double *a2, /* numerator coefficients */
+ double *b0, double *b1, double *b2, /* denominator coefficients */
+ double fc, /* Filter cutoff frequency */
+ double fs, /* sampling rate */
+ double *k, /* overall gain factor */
+ float *coef) /* pointer to 4 iir coefficients */
+{
+ /* 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(__MINGW__))
+bool DetectSIMD()
+{
+
+ 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;
+
+}
+
+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
+
+// 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
+Bit16s Moog1(Bit16s 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])
+
+ in -= q * hist[4]; //feedback
+ 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;
+
+ hist[4] = hist[4] - hist[4] * hist[4] * hist[4] * 0.166667f; //clipping
+ hist[0] = in;
+ //LOG_MSG("In %d Hist: %f", wg, hist[4]*32767);
+ return (Bit16s)(hist[4]*32767.0);
+}
+
+Bit16s Moog2(Bit16s 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 (Bit16s)(hist[4]*32767.0);
+}
+
+Bit16s simpleLowpass(Bit16s 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
+
+typedef struct {
+ float coef;
+ float lastval;
+} LOWPASS_STATE;
+
+typedef struct {
+ float tau;
+ float g;
+ float gsqu;
+ float *delbuf;
+ int bufsiz;
+ int bufpos;
+} COMB_STATE;
+
+typedef struct {
+ int lastsamp;
+ int cursamp;
+ int done;
+ LOWPASS_STATE lowpass[NUM_COMBS];
+ COMB_STATE comb[NUM_COMBS];
+ COMB_STATE allpass[2];
+} ST_REVERB;
+
+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);
+};
+
+/*
+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
+*/
+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 */
+ 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, use default g's and coef's */
+ 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;
+}
+
+
+INLINE void Reverb::run(float *lchan, float *rchan, float revfrac)
+ /* lchan,rchan non-reverberated input samples */
+ /* revfrac percent of output to be reverberated */
+ {
+ int i;
+ float lchanrev, rchanrev, tot=0;
+
+ //cout << " in run \n";
+
+ 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 = lchanrev ;
+ 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) ;
+
+ //*lchan = lchanrev*revfrac + (*lchan) ;
+ //*rchan = rchanrev*revfrac + (*rchan) ;
+
+ // cout << "lchan = \t" << *lchan <<endl;
+ //cout << "rchan = \t" << *rchan <<endl;
+
+
+}
+
+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 *
+ //lowpass(cstate->delbuf[cstate->bufpos], lpstate);
+ 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(__MINGW__))
+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
+ }
+
+ output *= ResonInv[revLevel];
+
+ return(output);
+}
+#else
+
+#ifdef 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);
+}
+#endif
+
+#endif
+
+float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel)
+{
+ float *hist2_ptr;
+ float output,new_hist;
+
+ hist2_ptr = hist1_ptr + 1; /* next history */
+
+ /* 1st number of coefficients array is overall input scale factor,
+ * or filter gain */
+ output = (input) * (*coef_ptr++);
+
+ output = output - *hist1_ptr * (*coef_ptr++);
+ new_hist = output - *hist2_ptr * (*coef_ptr++); /* poles */
+
+ output = new_hist + *hist1_ptr * (*coef_ptr++);
+ output = output + *hist2_ptr * (*coef_ptr++); /* zeros */
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+ hist1_ptr++;
+ hist2_ptr++;
+
+ // i = 1
+ output = output - *hist1_ptr * (*coef_ptr++);
+ new_hist = output - *hist2_ptr * (*coef_ptr++); /* poles */
+
+ output = new_hist + *hist1_ptr * (*coef_ptr++);
+ output = output + *hist2_ptr * (*coef_ptr++); /* zeros */
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+
+ output *= ResonInv[revLevel];
+
+ return(output);
+}
+
+#endif
+
+#if FILTER_64BIT == 1
+// 64-bit version
+long iir_filter(long input, __int64 *hist1_ptr, __int64 *coef_ptr)
+{
+ unsigned int i;
+ __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 (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
+
+ output = new_hist + ((history1 * (*coef_ptr++))>>20);
+ output = output + ((history2 * (*coef_ptr++))>>20); // zeros
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+ hist1_ptr++;
+ hist2_ptr++;
+ }
+
+ return(output>>20);
+}
+
+#endif
+
+#if FILTER_INT == 1
+long iir_filter(long input, signed long *hist1_ptr, signed long *coef_ptr)
+{
+ unsigned int i;
+ 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 (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++;
+ }
+
+ return(output>>10);
+}
+#endif
+
+/* end filter stuff */
+
+partialFormat PCM[54];
+partialTable PCMList[128];
+Bit32u PCMReassign[55];
+Bit32s PCMLoopTable[55];
+
+timbreParam drums[30];
+
+Bit16s romfile[PCMSIZE+GRAN]; // 256K
+static Bit16s chantable[32]; // 64 bytes
+static Bit16s miditable[9]; // 18 bytes
+
+static CPartialMT32 *partTable[MAXPARTIALS];
+static Bit32s PartialReserveTable[32];
+
+// For debuging partial allocation
+//static FILE *pInfo;
+struct partUsage {
+ int active[32];
+ int assign[32];
+ int owner[32];
+ int status[32];
+};
+
+static Bit32s activeChannels;
+
+// Some optimization stuff
+Bit32s divtable[256]; // 1K
+Bit32s smalldivtable[256]; // 1K
+static Bit16s freqtable[256]; // 512 bytes
+static Bit32u sqrtable[101]; // 404 bytes
+static Bit32s keytable[256]; // 1K
+static Bit32u wavtable[256]; // 1K
+Bit32u wavtabler[64][256]; // 64K
+Bit32u looptabler[16][16][256]; // 256K
+static Bit32u drumdelta[256]; // 1K
+Bit16s sintable[65536]; // 128K
+static Bit32s ptable[101]; // 404 bytes
+static Bit32s lfotable[101]; // 404 bytes
+Bit32s penvtable[16][128]; // 8K
+static Bit32s fildeptable[5][128]; // 3K
+static Bit32s timekeytable[5][128]; // 3K
+static Bit32s filveltable[128][128]; // 64K
+static Bit32s veltkeytable[5][128]; // 3K
+Bit32s pulsetable[101]; // 400 bytes
+Bit32s pulseoffset[101]; // 400 bytes
+Bit32s sawtable[128][128]; // 64K
+static Bit32s restable[201]; // 804 bytes
+//static Bit32s biastable[13]; // 56 bytes
+static Bit32s ampbiastable[16][128]; // 8K
+static Bit32s 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 Bit32s filtcoefffix[FILTERGRAN][32][16];
+#endif
+static float revtable[8]; // 16 bytes
+static Bit32s finetable[201]; // 804 bytes
+Bit32u lfoptable[101][128]; // 32K
+Bit32s ampveltable[128][64]; // 32K
+Bit32s pwveltable[15][128];
+static Bit32s envtimetable[101]; // 404 bytes
+static Bit32s decaytimetable[101]; // 404 bytes
+static Bit32s lasttimetable[101]; // 404 bytes
+Bit32s amptable[129]; // 516 bytes
+static Bit32s voltable[129]; // 516 bytes
+static float padjtable[51]; // 204 bytes
+static Bit32s bendtable[49]; // 195 bytes
+float ResonFactor[32];
+float ResonInv[32];
+
+Bit16s smallnoise[441]; // 4410 bytes at 44Khz
+Bit32s samplepos = 0;
+
+Bit16s* waveforms[4][256]; // 2K
+Bit32u waveformsize[4][256];
+Bit16s tmpforms[4][65536]; // 128K
+Bit16s finalforms[4][8192]; // 64K
+
+// Corresponding drum patches as matched to keyboard
+Bit8s 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
+Bit16s 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 };
+
+Bit8u PartialStruct[13] = {
+ 0, 0, 2, 2, 1, 3,
+ 3, 0, 3, 0, 2, 1, 3 };
+
+Bit8u PartMixStruct[13] = {
+ 0, 1, 0, 1, 1, 0,
+ 1, 3, 3, 2, 2, 2, 2 };
+
+Bit8u InitInstr[8] = {
+ 68, 48, 95, 78, 41, 3, 110, 122};
+
+Bit8s 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,
+};
+
+
+Bit32s 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
+Bit32u 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;
+
+Bit16s mastervolume;
+
+Bit32u curRevMode;
+Bit32u curRevTime;
+Bit32u curRevLevel;
+
+Bit32u partialsPlayed; // Variable containing the whole count of partials played
+Bit32u avgPartials; // Tally of average number of partials a second
+Bit32s partialChan[9]; // The count of partials played per channel
+
+#if SAVECUSTOM == 1
+Bit32u 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(Bit32u newRevMode, Bit32u newRevTime, Bit32u 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);
+ break;
+ case 1:
+ newReverb->setroomsize((float).5);
+ newReverb->setdamp((float).5);
+ break;
+ case 2:
+ newReverb->setroomsize((float).5);
+ newReverb->setdamp((float).1);
+ break;
+ case 3:
+ newReverb->setroomsize((float)1);
+ newReverb->setdamp((float).75);
+ break;
+ default:
+ newReverb->setroomsize((float).1);
+ newReverb->setdamp((float).5);
+ 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];
+
+ Bit32u bend;
+ Bit32s 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;
+
+ Bit32u P1Mix;
+ Bit32u 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);
+
+ //Bit32s getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly, bool inDecay);
+ //Bit32s getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly, bool inDecay);
+ //Bit32s getFiltEnvelope(Bit16s wg, dpoly::partialStatus *pStat, dpoly *poly, bool inDecay);
+
+ //void StartDecay(int envnum, Bit32s startval, dpoly::partialStatus *pStat, dpoly *poly);
+
+};
+
+void MidiChannel::SetHoldPedal(bool pedalval) {
+ holdpedal = pedalval;
+}
+
+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;
+ }
+ }
+
+}
+
+INLINE void StartDecay(int envnum, Bit32s 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;
+ }
+ tStat->envsize++;
+
+
+}
+
+
+
+
+
+INLINE Bit32s getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly) {
+ Bit32s 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));
+ if (tc < 0)
+ tc = 0;
+ if((tStat->envpos >= tStat->envsize) || (tc == 0)){
+ pStat->PCMDone = true;
+ pStat->isDecayed = true;
+ pStat->playPartial = false;
+ }
+ }
+
+ } 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 * (Bit32s)tcache->amplevel) >> 7;
+
+ }
+
+ // 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;
+ }
+ }
+ }
+
+ //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;
+ }
+ }
+ }
+
+
+
+ return tc;
+}
+
+
+INLINE Bit32s getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly) {
+ patchCache *tcache = pStat->tcache;
+ dpoly::partialStatus::envstatus *tStat = &pStat->envs[PITCHENV];
+
+ Bit32s 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));
+
+ }
+ tStat->prevlevel = tc;
+
+
+ }
+
+ return tc;
+
+}
+
+
+INLINE Bit32s getFiltEnvelope(Bit16s 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;
+ }
+
+ 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;
+
+
+ Bit32s 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;
+
+}
+
+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
+ }
+ break;
+ }
+ }
+ }
+ }
+ 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);
+}
+
+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;
+ break;
+ case 1:
+ pcache[t].PCMPartial = PartialStruct[(int)timSrc.common.pstruct12] & 0x1;
+ break;
+ case 2:
+ pcache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct34] >> 1) & 0x1;
+ break;
+ case 3:
+ pcache[t].PCMPartial = PartialStruct[(int)timSrc.common.pstruct34] & 0x1;
+ break;
+ default:
+ 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);
+ Bit32s pFine, tFine, fShift;
+ pFine = (Bit32s)timSrc.partial[t].wg.fine;
+ tFine = (Bit32s)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 Bit16s RingMod(Bit16s p1, Bit16s p2, bool useFirst) {
+ if(useFirst) {
+ //return (Bit16s)( ( ((float)p1/32767.0) * ((float)p2/32767.0) ) * 32767);
+ return (Bit16s)( ((Bit32s)p1 * (Bit32s)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 (Bit16s)( ((Bit32s)smallnoise[samplepos/100] * (Bit32s)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;
+ break;
+ case 0:
+ keyfollow = 0;
+ break;
+ case 1:
+ keyfollow = ((int)((tmpval*2-MIDDLEC*2)/2)*pcache[t].pitchkeyfollow)>>12;
+ 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;
+ break;
+ case 0:
+ keyfollow = tmpval;
+ break;
+ case 1:
+ keyfollow = ((int)((tmpval*2-(MIDDLEC*2))/2) * pcache[t].filtkeyfollow )>>12;
+ 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;
+ Bit64s 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
+ Bit64s 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 {
+ 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 {
+ 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);
+ }
+ 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);
+ }
+
+}
+
+MidiChannel *mchan[16];
+
+#endif
+
+
+bool CSynthMT32::InitTables(const char *baseDir ) {
+
+#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/24.0));
+ //LOG_MSG("KT %d = %d", f, keytable[f+108]);
+
+ }
+ float ff;
+ 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()) {
+ // TODO : Fail driver init
+ return false;
+
+ error("Unable to open waveforms.raw");
+ exit(0);
+ }
+
+ 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(Bit32s 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] = (Bit16s *)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] = (Bit16s *)malloc(waveformsize[i][f]);
+ for (int 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(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, tres;
+ 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] = Bit32s((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] = (Bit16s)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] = (Bit32s)(((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
+/*
+ Bit32u 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(char * baseDir, 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] += (Bit16s)(cos(dumbfire) * -ampsize);
+ tmpforms[3][fa>>3] += (Bit16s)(cos(sa-PI) * -ampsize);
+
+ tmpforms[0][fa>>4] += (Bit16s)(saw * -ampsize);
+ tmpforms[1][fa>>4] += (Bit16s)(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(const char *baseDir, SynthProperties useProp) {
+
+#ifdef NOMANSLAND
+
+ if (isOpen) return false;
+ int i;
+ // Initalize patch information
+ Bit8u sysexBuf[SYSEX_SIZE];
+ Bit16u syslen = 0;
+
+ bool inSys = false;
+
+ File fp;
+ Bit8u 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;
+
+ //exit(0);
+ }
+ break;
+ case 1:
+ fp.open("Preset2.syx");
+ if(!fp.isOpen()) {
+ // TODO : Fail driver init
+ error("Unable to open Preset2.syx");
+ return false;
+
+ //exit(0);
+ }
+ 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();
+
+ }
+
+ //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");
+ return false;
+
+ //exit(0);
+ }
+ 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);
+ }
+ }
+ //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;
+
+ //exit(0);
+ }
+
+ 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));
+ Bit32u patchstart = 0; //axtoi(tbuf);
+ Bit32u patchend = 0;
+ Bit32u patchcount = 0;
+ //Bit16s *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(NULL," \n\r");
+ if(cp != NULL) {
+ cp = strtok(NULL," \n\r");
+ if(cp != NULL) {
+ cp = strtok(NULL," \n\r");
+ if (cp !=NULL) {
+ int newpcm = atoi(cp);
+ PCMReassign[newpcm] = patchcount;
+ 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 (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;
+
+ //exit(0);
+ }
+
+ fPatch.close();
+
+
+ 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++) {
+ 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++) {
+ 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]];
+ PCMList[pat].loop = true;
+ PCMList[pat].aggSound = -1;
+ } else {
+ PCMList[pat].addr = 0;
+
+ //Calculate aggregate length
+ int aggsnd = (-PCMLoopTable[p])-1;
+ int tmplen = 0;
+ int sndpos = 0;
+ while(LoopPatterns[aggsnd][sndpos] != -1) {
+ tmplen += PCM[LoopPatterns[aggsnd][sndpos]].len;
+ sndpos++;
+ }
+
+ 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);
+
+ //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");
+ return false;
+
+ //exit(0);
+ }
+ i=0;
+ //Bit32s maxamp = 0;
+ while (!fIn.eof()) {
+ Bit16s 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;
+ } else {
+ if(order[u]>=8) {
+ bit = (c1 >> (7-(order[u]-8))) & 0x1;
+ } else {
+ bit = 0;
+ }
+ }
+ e = e | (bit << z);
+ --z;
+ }
+
+ //if( (e & 0x1) != 0) printf("Last bit = %d\n", e & 0x1);
+ //Bit16s 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));*/
+
+ 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};
+
+ e=0;
+ z = 15;
+ for(u=0;u<15;u++) {
+ if((order[u]<8) && (order[u]>=0)) {
+ 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;
+ }
+
+#ifdef MT32OUT
+ tmpc = e & 0xff; fOut.write(&tmpc, 1);
+ tmpc = (e >> 8) & 0x7f; fOut.write(&tmpc, 1);
+#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 / 341.32291666666666666666666666667);
+ //testval = -(testval / 400.00);
+ float vol = pow((float)8,(float)(testval / 20)) * 32767;
+
+ if (e>0) vol = -vol;
+
+ romfile[i] = (Bit16s)vol;
+
+#ifdef MT32OUT
+ tmpc = (Bit16s)vol & 0xff; fOutb.write(&tmpc, 1);
+ tmpc = (Bit16s)vol >> 8; fOutb.write(&tmpc, 1);
+#endif
+
+ i++;
+ }
+ //LOG_MSG("PCM amp = %d", maxamp);
+#ifdef MT32OUT
+ fOutb.close();
+ fOut.close();
+#endif
+ fIn.close();
+ int tmp;
+ for(tmp=0;tmp<16;tmp++) {
+ if((tmp>=1) && (tmp<=9)) {
+ chantable[tmp] = tmp-1;
+ } else {
+ chantable[tmp] = -1;
+ }
+ }
+ chantable[10] = 8;
+ for(i=0;i<128;i++) {
+ mt32ram.params.pSettings[i].timbreGroup = i >> 6;
+ mt32ram.params.pSettings[i].timbreNum = i & 63;
+ }
+
+ // For resetting mt32 mid-execution
+ memcpy(&mt32default, &mt32ram, sizeof(mt32ram));
+
+ if (!InitTables(baseDir)) return false;
+ if(myProp.UseDefault) {
+ InitReverb(0,5,SETRATE);
+ } else {
+ InitReverb(myProp.RevType, myProp.RevTime,SETRATE);
+ }
+
+ for(i=0;i<10;i++) {
+ mchan[i] = new MidiChannel(SETRATE,i);
+
+ if(i<8) mchan[i]->SetPatch(InitInstr[i],-1);
+ if(i>8) mchan[i]->SetPatch(InitInstr[i-9],-1);
+ }
+ activeChannels = 0;
+
+#ifdef HAVE_X86
+ bool useSSE = false, use3DNow = false;
+
+ 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");
+ usefilter = &iir_filter_3dnow;
+ }
+
+ if(useSSE) {
+ debug(1, "MT-32 using SIMD (Intel SSE) extensions\n");
+ usefilter = &iir_filter_sse;
+ usingSIMD = true;
+ }
+#endif
+
+ isOpen=true;
+ isEnabled=false;
+
+#endif
+
+ return true;
+}
+
+void CSynthMT32::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]);
+ }
+ }
+
+#endif
+
+ isOpen=false;
+
+
+}
+
+void CSynthMT32::PlayMsg(Bit32u msg) {
+
+#ifdef NOMANSLAND
+
+ 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);
+ return;
+
+ }
+ if(chan>8) return;
+
+
+ int patch;
+ Bit32u bend;
+
+ //LOG_MSG("Midi code: 0x%x",msg);
+ switch (code) {
+ case 0x80:
+ //LOG_MSG("Note OFF - Channel %d",chan);
+ mchan[chan]->StopNote(note,velocity);
+ 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);
+ } else {
+ mchan[chan]->StopNote(note,velocity);
+ }
+
+
+ break;
+ case 0xb0: // Control change
+ switch (note) {
+ case 0x1: // Modulation
+ //LOG_MSG("Modulation: %d", velocity);
+ mchan[chan]->SetModulation(velocity);
+ break;
+ case 0xb:
+ //LOG_MSG("Expression set: %d", velocity);
+ mchan[chan]->SetVolume(velocity);
+ break;
+ case 0x7: // Set volume
+ //if(chan!=3) return;
+ //LOG_MSG("Volume set: %d", velocity);
+ mchan[chan]->SetVolume(velocity);
+ break;
+ case 0xa: // Pan
+ mchan[chan]->SetPan(velocity);
+ break;
+ case 0x40: // Hold pedal
+ if(velocity<64) {
+ mchan[chan]->SetHoldPedal(false);
+ mchan[chan]->StopPedalHold();
+ } else {
+ mchan[chan]->SetHoldPedal(true);
+ }
+ break;
+
+ case 0x7b: // All notes off
+
+ for(h=0;h<MAXPOLY;h++) {
+ mchan[chan]->StopNote(-1,0);
+ }
+ break;
+ case 0x79: // Reset all controllers
+ break;
+
+ default:
+ //LOG_MSG("Control code: 0x%x - vel %x",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);
+ break;
+ case 0xe0: // Pitch bender
+ bend = (velocity << 7) | (note);
+ //LOG_MSG("Pitch bender %x", bend);
+ mchan[chan]->SetBend(bend);
+ break;
+ default:
+ //LOG_MSG("Undef Midi code: 0x%x - %x - %x",code, note, velocity);
+
+ break;
+ }
+
+#endif
+ //midiOutShortMsg(m_out, msg);
+}
+
+void CSynthMT32::PlaySysex(Bit8u * sysex,Bit32u len) {
+
+#ifdef NOMANSLAND
+
+ Bit32u addr;
+ Bit32u *header;
+ unsigned int off;
+ int m;
+ header = (Bit32u *)(sysex+1);
+ //int dummy = 0;
+ Bit32s lens = len;
+
+
+ // For some reason commands in IMuseInternal::initMT32 do not have prefix byte
+ if(READ_LE_UINT32(header) != 0x12161041) {
+ if(READ_LE_UINT32(sysex) == 0x12161041) {
+ header = (Bit32u *)sysex;
+ sysex--; // We don't access sysex[0], so it's safe
+ }
+ }
+
+ if(READ_LE_UINT32(header) == 0x12161041) {
+ 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];
+ }
+ //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;
+ }
+ }
+ }
+ //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);
+ }
+ 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) {
+ case 0:
+ calcoff = tc * sizeof(timbreParam);
+ pn = tc;
+ break;
+ case 1:
+ calcoff = (tc+ 64) * sizeof(timbreParam);
+ pn = tc + 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];
+ }
+ //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));
+
+ 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];
+ }
+ }
+
+ //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(((Bit32u)mt32ram.params.system.reverbMode != curRevMode) || ((Bit32u)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;
+ }
+ }
+
+
+ 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++;
+ }
+ }
+ //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);
+ Bit16s tv = (Bit16s)((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 (Bit32u m1=0;m1<MAXPARTIALS;m1++) partTable[m1]->isActive = false;
+
+ memcpy(&mt32ram, &mt32default, sizeof(mt32ram));
+ isEnabled = false;
+ }
+
+
+ } else {
+ // Header not intended for Roland MT-32
+ }
+
+#endif
+
+}
+
+
+int CSynthMT32::DumpSysex(char *filename) {
+ File fp;
+ char tmpc;
+ fp.open(filename,File::kFileWriteMode);
+ if(!fp.isOpen())
+ return -1;
+
+ int 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);
+
+ int useaddr = patchnum * 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(&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);
+ //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);
+
+ //End of sysex
+ tmpc = 0xf7; fp.write(&tmpc, 1);
+ }
+ fp.close();
+ //LOG_MSG("Wrote temp patches to %s", usefile);
+
+ return 0;
+}
+
+
+
+static Bit16s 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 };
+#endif
+
+void CSynthMT32::MT32_CallBack(Bit8u * stream,Bit32u len, int volume) {
+
+#ifdef NOMANSLAND
+ Bit32s i,m;
+ Bit16s *snd, *useBuf;
+ Bit32u outlen;
+ snd = (Bit16s *)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];
+ }
+ fwrite(&outUsage,sizeof(outUsage),1,pInfo);*/
+
+ for(i=0;i<MAXPARTIALS;i++) partTable[i]->age++;
+
+ for(i=0;i<MAXPARTIALS;i++) {
+
+ if(partTable[i]->produceOutput(tmpBuffer,outlen)==true) {
+#if USE_MMX == 0
+ Bit16s *tmpoff = snd;
+ int q = 0;
+ for(m=0;m<(Bit32s)outlen;m++) {
+ tmpoff[q] += (Bit16s)(((Bit32s)tmpBuffer[q] * (Bit32s)mastervolume)>>15);
+ q++;
+ tmpoff[q] += (Bit16s)(((Bit32s)tmpBuffer[q] * (Bit32s)mastervolume)>>15);
+ q++;
+
+ }
+#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
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop4
+ emms
+ }
+#else
+ atti386_produceOutput1(tmplen, mastervolume, useBuf, snd);
+#endif
+#endif
+ }
+ }
+
+ if(myProp.UseReverb) {
+#if USE_MMX == 3
+ if(!usingSIMD) {
+#endif
+ m=0;
+ for(i=0;i<(Bit32s)len;i++) {
+ sndbufl[i] = (float)snd[m] / 32767.0;
+ m++;
+ sndbufr[i] = (float)snd[m] / 32767.0;
+ m++;
+ }
+ newReverb->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
+ m=0;
+ for(i=0;i<(Bit32s)len;i++) {
+ snd[m] = (Bit16s)(outbufl[i] * 32767.0);
+ m++;
+ snd[m] = (Bit16s)(outbufr[i] * 32767.0);
+ 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;
+ }
+ }
+ }
+ }
+#endif
+
+#if MONITORPARTIALS == 1
+ samplepos+=outlen;
+ if(samplepos>SETRATE*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]);
+
+ }
+
+#endif
+
+
+#endif
+
+
+}
diff --git a/backends/midi/mt32/synth.h b/backends/midi/mt32/synth.h
new file mode 100644
index 0000000000..1978a17a2f
--- /dev/null
+++ b/backends/midi/mt32/synth.h
@@ -0,0 +1,170 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ * Based on Tristan's conversion of Canadacow's code
+ *
+ * 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$
+ *
+ */
+
+#if !defined __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
+
+extern const char *rom_path;
+
+#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();
+
+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
+ // following parameters
+ 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;
+ // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
+ int RevTime;
+ // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
+ int RevLevel;
+};
+
+#ifndef BOOL
+ #define BOOL bool
+#endif
+#ifndef TRUE
+ #define TRUE true
+#endif
+#ifndef FALSE
+ #define FALSE false
+#endif
+
+// This is the specification of the Callback routine used when calling the RecalcWaveforms
+// function
+typedef void (*recalcStatusCallback)(int percDone);
+
+// This external function recreates the base waveform file (waveforms.raw) using a specifed
+// sampling rate. The callback routine provides interactivity to let the user know what
+// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the
+// callback routine, no status is reported.
+BOOL RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
+
+typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
+extern iir_filter_type usefilter;
+
+extern partialFormat PCM[54];
+extern Bit16s romfile[262656];
+extern Bit32s divtable[256];
+extern Bit32s smalldivtable[256];
+extern Bit32u wavtabler[64][256];
+extern Bit32u looptabler[16][16][256];
+extern Bit16s sintable[65536];
+extern Bit32s penvtable[16][128];
+extern Bit32s pulsetable[101];
+extern Bit32s pulseoffset[101];
+extern Bit32s sawtable[128][128];
+extern float filtcoeff[FILTERGRAN][32][16];
+extern Bit32u lfoptable[101][128];
+extern Bit32s ampveltable[128][64];
+extern Bit32s amptable[129];
+extern Bit16s smallnoise[441];
+extern Bit32s samplepos;
+extern Bit16s* waveforms[4][256];
+extern Bit32u waveformsize[4][256];
+extern Bit8s LoopPatterns[16][16];
+extern int drumPan[30][2];
+extern float ResonFactor[32];
+extern float ResonInv[32];
+
+extern Bit32s getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly);
+extern Bit32s getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly);
+extern Bit32s getFiltEnvelope(Bit16s wg, dpoly::partialStatus *pStat, dpoly *poly);
+
+class CSynthMT32 {
+private:
+
+ unsigned char initmode;
+ bool isOpen;
+ SynthProperties myProp;
+
+ bool InitTables(const char * baseDir);
+
+public:
+ CSynthMT32() : isOpen(false) {};
+
+ // Used to initialized the MT-32. The baseDir parameter points to the location in the
+ // filesystem where the ROM and data files are located. The second parameter specifies
+ // properties for the synthesizer, as outlined in the structure above.
+ // Returns TRUE if initialization was sucessful, otherwise returns FALSE.
+ bool ClassicOpen(const char *baseDir, SynthProperties useProp);
+
+ // Closes the MT-32 and deallocates any memory used by the synthesizer
+ void Close(void);
+
+ // Sends a 4-byte MIDI message to the MT-32 for immediate playback
+ void PlayMsg(Bit32u msg);
+
+ // Sends a string of Sysex commands to the MT-32 for immediate interpretation
+ void PlaySysex(Bit8u * sysex, Bit32u len);
+
+ // Save the system state to a sysex file specified by filename
+ int DumpSysex(char *filename);
+
+ // 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(Bit8u * stream, Bit32u len, int volume);
+
+};
+
+#endif