aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2004-10-21 22:37:37 +0000
committerEugene Sandulenko2004-10-21 22:37:37 +0000
commit9d0b746aaaaf78cfcc18e866e4375ad1de533bc4 (patch)
treef6570ed9ff19e0111704e638c6eec19e0f454f15
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
-rw-r--r--README62
-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
-rw-r--r--backends/module.mk10
-rw-r--r--base/gameDetector.cpp2
-rwxr-xr-xconfigure22
-rw-r--r--doc/07.tex2
-rw-r--r--doc/07_02.tex6
-rw-r--r--doc/07_02a.tex18
-rw-r--r--doc/10.tex3
-rw-r--r--scumm/scumm.cpp5
-rw-r--r--scummvm.62
-rw-r--r--sound/mididrv.cpp1
-rw-r--r--sound/mididrv.h4
21 files changed, 7066 insertions, 29 deletions
diff --git a/README b/README
index 7f0fd3d18e..62f7946e57 100644
--- a/README
+++ b/README
@@ -30,11 +30,12 @@ Table of Contents:
* 6.1 Autosaves
7.0) Music and Sound
* 7.1 Adlib emulation
- * 7.2 MIDI emulation
- * 7.3 Native MIDI support
- * 7.4 UNIX native & ALSA sequencer support
- * 7.5 Using compressed audiofiles (MP3, Ogg Vorbis, Flac)
- * 7.6 Output sample rate
+ * 7.2 MT-32 emulation
+ * 7.3 MIDI emulation
+ * 7.4 Native MIDI support
+ * 7.5 UNIX native & ALSA sequencer support
+ * 7.6 Using compressed audiofiles (MP3, Ogg Vorbis, Flac)
+ * 7.7 Output sample rate
8.0) Configuration Files
9.0) Compiling
X.X) Credits
@@ -659,6 +660,7 @@ manual configuration. If you ARE using MIDI, you have several different
choices of output, depending on your operating system and configuration.
adlib - Uses internal Adlib Emulation (default)
+ mt32 - Uses internal MT-32 Emulation
pcjr - Uses internal PCjr Emulation
pcspk - Uses internal PC Speaker Emulation
towns - Uses FM-TOWNS YM2612 Emulation
@@ -682,25 +684,38 @@ By default an Adlib card will be emulated and ScummVM will output the music
as sampled waves. This is the default mode for most games, and offers the
best compatibility between machines and games.
+7.2) Playing sound with MT-32 emulation:
+---- -----------------------------------
+Some games which contain MIDI music data also have improved tracks designed
+for MT-32 sound module. ScummVM can now emulate this card, however you should
+provide original MT-32 ROMs to make it work. Put the roms in game directory or
+directory specified by extrapath.
+
+You don't need to specify --native-mt32 with this driver, as it automatically
+gets turned on.
+
+NOTE: You need to have enough processor power to use this emulator as it uses
+heavy floating-point computations.
+
-7.2) Playing sound with MIDI emulation:
+7.3) Playing sound with MIDI emulation:
---- ----------------------------------
Some games (such as Sam and Max) only contain MIDI music data. This once
prevented music for these games from working on platforms that do not support
MIDI, or soundcards that do not provide MIDI drivers (e.g, many soundcards will
not play MIDI under Linux). ScummVM can now emulate MIDI mode using sampled
-waves and Adlib emulation using the -eadlib option. However, if you are capable
-of using native MIDI, we recommend using one of the MIDI modes below for best
-sound.
+waves and Adlib or MT-32 emulation using the -eadlib or -emt32 options respectively.
+However, if you are capable of using native MIDI, we recommend using one of the MIDI
+modes below for best sound.
-7.3) Playing sound with Native MIDI:
+7.4) Playing sound with Native MIDI:
---- -------------------------------
Use the appropriate -e<mode> command line option from the list above to
select your preferred MIDI device. For example, if you wish to use the
Windows MIDI driver, use the -ewindows option.
-7.4.0) Playing sound with Sequencer MIDI: [UNIX ONLY]
+7.5.0) Playing sound with Sequencer MIDI: [UNIX ONLY]
------ ----------------------------------
If your soundcard driver supports a sequencer, you may set the environment
variable "SCUMMVM_MIDI" to your sequencer device - e.g., /dev/sequencer
@@ -713,7 +728,7 @@ performance and quality than Adlib emulation. However, for those systems where
sequencer support does not work, you can always fall back on Adlib emulation.
-7.4.1) Playing sound with ALSA sequencer: [UNIX ONLY]
+7.5.1) Playing sound with ALSA sequencer: [UNIX ONLY]
------ ----------------------------------
If you have installed the ALSA driver with the sequencer support, then
set the environment variable SCUMMVM_PORT or the config file parameter
@@ -762,7 +777,7 @@ Asking FluidSynth to become an ALSA sequencer (using SoundFonts):
Once either TiMidity or FluidSynth are running, use the 'aconnect -o -l'
command as described earlier in this section.
-7.5.0) Using MP3 files for CD audio:
+7.6.0) Using MP3 files for CD audio:
------ -----------------------------
Use LAME or some other MP3 encoder to rip the cd audio tracks to files. Name
the files track1.mp3 track2.mp3 etc. ScummVM must be compiled with MAD support
@@ -773,7 +788,7 @@ following LAME command line:
lame -t -q 0 -b 96 track1.wav track1.mp3
-7.5.1) Using Ogg Vorbis files for CD audio:
+7.6.1) Using Ogg Vorbis files for CD audio:
------ ------------------------------------
Use oggenc or some other vorbis encoder to encode the audio tracks to files.
Name the files track1.ogg track2.ogg etc. ScummVM must be compiled with vorbis
@@ -784,7 +799,7 @@ command line with the value after q specifying the desired quality from 0 to 10:
oggenc -q 5 track1.wav
-7.5.2) Using Flac files for CD audio:
+7.6.2) Using Flac files for CD audio:
------ ------------------------------------
Use flac or some other flac encoder to encode the audio tracks to files.
Name the files track1.flac track2.flac etc. In your filesystem only allows
@@ -799,7 +814,7 @@ Remember that the quality is always the same, varying encoder options will only
affect the encoding time and resulting filesize.
-7.5.3) Compressing MONSTER.SOU with MP3:
+7.6.3) Compressing MONSTER.SOU with MP3:
------ ---------------------------------
You need LAME, and our extract util from the scummvm-tools package to perform
this task, and ScummVM must be compiled with MAD support.
@@ -810,7 +825,7 @@ Eventually you will have a much smaller monster.so3 file, copy this file
to your game directory. You can safely remove the monster.sou file.
-7.5.4) Compressing MONSTER.SOU with Ogg Vorbis:
+7.6.4) Compressing MONSTER.SOU with Ogg Vorbis:
------ ----------------------------------------
As above, but ScummVM must be compiled with OGG support. Run:
@@ -821,7 +836,7 @@ game directory. Ogg encoding may take a considerable longer amount of time
than MP3, so have a good book handy.
-7.5.5) Compressing MONSTER.SOU with Flac:
+7.6.5) Compressing MONSTER.SOU with Flac:
------ ----------------------------------------
As above, but ScummVM must be compiled with Flac support. Run:
@@ -835,7 +850,7 @@ filesize - 1152 seems to be a good value for those kind of soundfiles. Be sure
to read the encoder documentation before you use other values.
-7.5.6) Compressing sfx/speech in Simon the Sorcerer 1 and 2
+7.6.6) Compressing sfx/speech in Simon the Sorcerer 1 and 2
------ ----------------------------------------------------
Use our simon2mp3 util from the scummvm-tools package to perform this task.
You can choose between multiple target formats, but note that you can only use
@@ -861,7 +876,7 @@ For Flac add --flac and optional parameters, i.e.
Eventually you will have a much smaller *.mp3, *.ogg or *.fla file, copy this
file to your game dir. You can safely remove the old file.
-7.5.7) Compressing speech/music in Broken Sword 1
+7.6.7) Compressing speech/music in Broken Sword 1
------ ------------------------------------------
The sword1mp3 tool from the scummvm-tools package can encode music and speech
to MP3 as well as Ogg Vorbis.
@@ -876,7 +891,7 @@ of MP3.
Use "sword1mp3 --help" to get a full list of the options.
-7.5.8) Compressing speech/music in Broken Sword 2
+7.6.8) Compressing speech/music in Broken Sword 2
------ ------------------------------------------
Use our sword2mp3 util from the scummvm-tools package to perform this task.
You can choose between multiple target formats, but note that you can only use
@@ -900,7 +915,7 @@ Broken Sword 2. It will not work with any of the other *.clu files, nor will it
work with the speech files from Broken Sword 1.
-7.6) Output sample rate:
+7.7) Output sample rate:
---- -------------------
The output sample rate tells ScummVM how many sound samples to play per channel
@@ -1181,9 +1196,12 @@ X.X) Credits:
Special thanks to:
Sander Buskens - For his work on the initial reversing of Monkey2
+ Canadacow - For his MT-32 emulator
Kevin Carnes - For Scumm16, the basis of ScummVM older gfx codec
+ Jezar - For his freeverb filter implementation
Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports
Jimmi Thogersen - For ScummRev, and much obscure code/documentation
+ Tristan - For his Linux port of MT-32 emulator
Tony Warriner and everyone at Revolution Software Ltd. for sharing
with us the source of some of their brilliant games, allowing us to
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
diff --git a/backends/module.mk b/backends/module.mk
index 45a9206323..85e128b7dd 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -12,14 +12,20 @@ MODULE_OBJS := \
backends/midi/seq.o \
backends/midi/alsa.o \
backends/midi/windows.o \
- backends/midi/ym2612.o
+ backends/midi/ym2612.o \
+ backends/midi/mt32/mt32.o \
+ backends/midi/mt32/partial.o \
+ backends/midi/mt32/synth.o \
+ backends/midi/mt32/freeverb.o
+
MODULE_DIRS += \
backends \
backends/fs/posix \
backends/fs/morphos \
backends/fs/windows \
- backends/midi
+ backends/midi \
+ backends/midi/mt32
# Include common rules
include $(srcdir)/common.rules
diff --git a/base/gameDetector.cpp b/base/gameDetector.cpp
index bf118a519f..708097d2fd 100644
--- a/base/gameDetector.cpp
+++ b/base/gameDetector.cpp
@@ -599,6 +599,8 @@ MidiDriver *GameDetector::createMidi(int midiDriver) {
// driver.
case MD_ADLIB: return NULL;
+ case MD_MT32: return MidiDriver_MT32_create(g_engine->_mixer, ConfMan.get("extrapath").c_str());
+
case MD_TOWNS: return MidiDriver_YM2612_create(g_engine->_mixer);
// Right now PC Speaker and PCjr are handled
diff --git a/configure b/configure
index 5ae821e1b3..d5d3811d19 100755
--- a/configure
+++ b/configure
@@ -51,6 +51,7 @@ _nasmpath="$PATH"
NASMFLAGS=""
NASM=""
_prefix=/usr/local
+_have_x86=""
_srcdir=`dirname $0`
@@ -660,6 +661,26 @@ EOF
rm -f tmp_endianness_check$EXEEXT tmp_endianness_check.cpp
#
+ # Check whether we can use x86 asm routines
+ #
+ echo_n "Running on x86... "
+ case $_host_cpu in
+ i386|i486|i586|i686)
+ _have_x86=yes
+ ;;
+ *)
+ _have_x86=no
+ ;;
+ esac
+
+ if test "$_have_x86" = yes ; then
+ _def_x86='#define HAVE_X86'
+ else
+ _def_x86='#undef HAVE_X86'
+ fi
+ echo "$_have_x86"
+
+ #
# Check whether memory alignment is required
#
echo_n "Alignment required... "
@@ -1019,6 +1040,7 @@ cat > config.h << EOF
$_def_endianness
$_def_align
+$_def_x86
$_def_linupy
diff --git a/doc/07.tex b/doc/07.tex
index 6d9fecb670..cad52fcab0 100644
--- a/doc/07.tex
+++ b/doc/07.tex
@@ -12,6 +12,7 @@ choices of output, depending on your operating system and configuration.
\begin{tabular}[h]{ll}
adlib & Uses internal Adlib Emulation (default)\\
+ mt32 & Uses internal MT-32 Emulation\\
pcjr & Uses internal PCjr Emulation \\
pcspk & Uses internal PC Speaker Emulation\\
towns & Uses FM-TOWNS YM2612 Emulation\\
@@ -31,6 +32,7 @@ for example:
\end{verbatim}
\input {07_01.tex}
+\input {07_02a.tex}
\input {07_02.tex}
\input {07_03.tex}
\input {07_04.tex}
diff --git a/doc/07_02.tex b/doc/07_02.tex
index d505e8d26e..16a7444e67 100644
--- a/doc/07_02.tex
+++ b/doc/07_02.tex
@@ -10,6 +10,6 @@ Some games (such as Sam and Max) only contain MIDI music data. This once
prevented music for these games from working on platforms that do not support
MIDI, or soundcards that do not provide MIDI drivers (e.g, many soundcards will
not play MIDI under Linux). ScummVM can now emulate MIDI mode using sampled
-waves and Adlib emulation using the -eadlib option. However, if you are capable
-of using native MIDI, we recommend using one of the MIDI modes below for best
-sound.
+waves and Adlib or MT-32 emulation using the -eadlib or -emt32 options respectively.
+However, if you are capable of using native MIDI, we recommend using one of the
+MIDI modes below for best sound.
diff --git a/doc/07_02a.tex b/doc/07_02a.tex
new file mode 100644
index 0000000000..27baec1c93
--- /dev/null
+++ b/doc/07_02a.tex
@@ -0,0 +1,18 @@
+
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: "readme"
+%%% End:
+
+\subsection{Playing sound with MT-32 emulation}
+
+Some games which contain MIDI music data also have improved tracks designed
+for MT-32 sound module. ScummVM can now emulate this card, however you should
+provide original MT-32 ROMs to make it work. Put the roms in game directory or
+directory specified by extrapath.
+
+You don't need to specify --native-mt32 with this driver, as it automatically
+gets turned on.
+
+\textbf{NOTE:} You need to have enough processor power to use this emulator as
+ it uses heavy floating-point computations.
diff --git a/doc/10.tex b/doc/10.tex
index 89055f15a6..574ff79675 100644
--- a/doc/10.tex
+++ b/doc/10.tex
@@ -65,9 +65,12 @@ Thanks!
\item Special thanks to:\\
\begin{tabular}{p{4cm}l}
Sander Buskens & For his work on the initial reversing of Monkey2\\
+ Canadacow & For his MT-32 emulator\\
Kevin Carnes & For Scumm16, the basis of ScummVM older gfx codec\\
+ Jezar & For his freeverb filter implementation\\
Jim Leiterman & Various info on his FM-TOWNS/Marty SCUMM ports\\
Jimmi Thogersen & For ScummRev, and much obscure code/documentation\\
+ Tristan & For his Linux port of MT-32 emulator\\
\end{tabular}
Tony Warriner and everyone at Revolution Software Ltd. for sharing
diff --git a/scumm/scumm.cpp b/scumm/scumm.cpp
index 14b4ae8b61..0b7437ec97 100644
--- a/scumm/scumm.cpp
+++ b/scumm/scumm.cpp
@@ -1343,7 +1343,7 @@ void ScummEngine_v90he::scummInit() {
void ScummEngine::setupMusic(int midi) {
_midiDriver = GameDetector::detectMusicDriver(midi);
- _native_mt32 = ConfMan.getBool("native_mt32");
+ _native_mt32 = (ConfMan.getBool("native_mt32") || (_midiDriver == MD_MT32));
#ifndef __GP32__ //ph0x FIXME, "quick dirty hack"
/* Bind the mixer to the system => mixer will be invoked
@@ -1393,6 +1393,9 @@ void ScummEngine::setupMusic(int midi) {
}
if (midi == MDT_TOWNS)
_imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
+ if (_midiDriver == MD_MT32) {
+ _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
+ }
_imuse->set_music_volume(ConfMan.getInt("music_volume"));
}
}
diff --git a/scummvm.6 b/scummvm.6
index f19b4dcc35..42b3e266da 100644
--- a/scummvm.6
+++ b/scummvm.6
@@ -62,6 +62,8 @@ Specify music output where \fI<mode>\fP can be one of:
.br
\fBadlib\fP Uses internal Adlib Emulation (default)
.br
+\fBmt32\fP Uses internal MT-32 Emulation
+.br
\fBwindows\fP Windows MIDI. Uses built-in sequencer.
.br
\fBseq\fP Uses /dev/sequencer for MIDI, *nix users.
diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp
index 9bf41726a5..919840c30f 100644
--- a/sound/mididrv.cpp
+++ b/sound/mididrv.cpp
@@ -57,6 +57,7 @@ static const struct MidiDriverDescription midiDrivers[] = {
{"towns", "FM Towns", MD_TOWNS},
{"pcspk", "PC Speaker", MD_PCSPK},
{"pcjr", "IBM PCjr", MD_PCJR},
+ {"mt32", "MT-32", MD_MT32},
#if defined(__PALM_OS__)
{"ypa1", "Yamaha Pa1", MD_YPA1},
diff --git a/sound/mididrv.h b/sound/mididrv.h
index c3a98aede7..9300e47b3a 100644
--- a/sound/mididrv.h
+++ b/sound/mididrv.h
@@ -47,7 +47,8 @@ enum {
MD_PCJR = 12,
MD_TOWNS = 13,
MD_YPA1 = 14, // PalmOS
- MD_ZODIAC = 15 // PalmOS
+ MD_ZODIAC = 15, // PalmOS
+ MD_MT32 = 16
};
/** Convert a string containing a music driver name into MIDI Driver type. */
@@ -177,6 +178,7 @@ extern MidiDriver *MidiDriver_CORE_create();
extern MidiDriver *MidiDriver_ETUDE_create();
extern MidiDriver *MidiDriver_ALSA_create();
extern MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer);
+extern MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer, const char *path);
extern MidiDriver *MidiDriver_YamahaPa1_create();
extern MidiDriver *MidiDriver_Zodiac_create();