aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sfx/softseq
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/sfx/softseq')
-rw-r--r--engines/sci/sfx/softseq/Makefile.am5
-rw-r--r--engines/sci/sfx/softseq/SN76496.c248
-rw-r--r--engines/sci/sfx/softseq/amiga.c658
-rw-r--r--engines/sci/sfx/softseq/fluidsynth.c262
-rw-r--r--engines/sci/sfx/softseq/fmopl.c1144
-rw-r--r--engines/sci/sfx/softseq/fmopl.h168
-rw-r--r--engines/sci/sfx/softseq/mt32.cpp228
-rw-r--r--engines/sci/sfx/softseq/mt32/Makefile.am5
-rw-r--r--engines/sci/sfx/softseq/mt32/freeverb.cpp310
-rw-r--r--engines/sci/sfx/softseq/mt32/freeverb.h239
-rw-r--r--engines/sci/sfx/softseq/mt32/i386.cpp849
-rw-r--r--engines/sci/sfx/softseq/mt32/i386.h49
-rw-r--r--engines/sci/sfx/softseq/mt32/mt32_file.cpp112
-rw-r--r--engines/sci/sfx/softseq/mt32/mt32_file.h67
-rw-r--r--engines/sci/sfx/softseq/mt32/mt32emu.h70
-rw-r--r--engines/sci/sfx/softseq/mt32/part.cpp632
-rw-r--r--engines/sci/sfx/softseq/mt32/part.h113
-rw-r--r--engines/sci/sfx/softseq/mt32/partial.cpp960
-rw-r--r--engines/sci/sfx/softseq/mt32/partial.h148
-rw-r--r--engines/sci/sfx/softseq/mt32/partialManager.cpp272
-rw-r--r--engines/sci/sfx/softseq/mt32/partialManager.h56
-rw-r--r--engines/sci/sfx/softseq/mt32/structures.h284
-rw-r--r--engines/sci/sfx/softseq/mt32/synth.cpp1199
-rw-r--r--engines/sci/sfx/softseq/mt32/synth.h300
-rw-r--r--engines/sci/sfx/softseq/mt32/tables.cpp749
-rw-r--r--engines/sci/sfx/softseq/mt32/tables.h116
-rw-r--r--engines/sci/sfx/softseq/opl2.c718
-rw-r--r--engines/sci/sfx/softseq/pcspeaker.c184
-rw-r--r--engines/sci/sfx/softseq/softsequencers.c71
29 files changed, 10216 insertions, 0 deletions
diff --git a/engines/sci/sfx/softseq/Makefile.am b/engines/sci/sfx/softseq/Makefile.am
new file mode 100644
index 0000000000..af7f8ca2c9
--- /dev/null
+++ b/engines/sci/sfx/softseq/Makefile.am
@@ -0,0 +1,5 @@
+noinst_LIBRARIES = libscisoftseq.a
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+libscisoftseq_a_SOURCES = softsequencers.c pcspeaker.c SN76496.c fmopl.c opl2.c amiga.c \
+ fluidsynth.c
+EXTRA_DIST = fmopl.h
diff --git a/engines/sci/sfx/softseq/SN76496.c b/engines/sci/sfx/softseq/SN76496.c
new file mode 100644
index 0000000000..cb32b89f0c
--- /dev/null
+++ b/engines/sci/sfx/softseq/SN76496.c
@@ -0,0 +1,248 @@
+/***************************************************************************
+ SN76496.c Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* Tandy/PCJr sequencer for FreeSCI */
+
+#include "../softseq.h"
+#include <sci_midi.h>
+
+#define FREQUENCY 44100
+#define CHANNELS_NR 3
+#define VOLUME_SHIFT 3
+
+static int global_volume = 100; /* Base volume */
+static int volumes[CHANNELS_NR] = { 100, 100, 100 };
+static int notes[CHANNELS_NR] = {0, 0, 0}; /* Current halftone, or 0 if off */
+static int freq_count[CHANNELS_NR] = {0, 0, 0};
+static int channel_assigner = 0;
+static int channels_assigned = 0;
+static int chan_nrs[CHANNELS_NR] = {-1, -1, -1};
+
+extern sfx_softseq_t sfx_softseq_pcspeaker;
+/* Forward-declare the sequencer we are defining here */
+
+
+static int
+SN76496_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+SN76496_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2,
+ int patch2_len)
+{
+ return SFX_OK;
+}
+
+static void
+SN76496_exit(sfx_softseq_t *self)
+{
+}
+
+static void
+SN76496_event(sfx_softseq_t *self, byte command, int argc, byte *argv)
+{
+ int i;
+ int chan = -1;
+#if 0
+ fprintf(stderr, "Note [%02x : %02x %02x]\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0);
+#endif
+ if ((command & 0xe0) == 0x80) {
+ int chan_nr = command & 0xf;
+
+ /* First, test for channel having been assigned already */
+ if (channels_assigned & (1 << chan_nr)) {
+ /* Already assigned this channel number: */
+ for (i = 0; i < CHANNELS_NR; i++)
+ if (chan_nrs[i] == chan_nr) {
+ chan = i;
+ break;
+ }
+ } else {
+ /* Assign new channel round-robin */
+
+ /* Mark channel as unused: */
+ if (chan_nrs[channel_assigner] >= 0)
+ channels_assigned &= ~(1 << chan_nrs[channel_assigner]);
+
+ /* Remember channel: */
+ chan_nrs[channel_assigner] = chan_nr;
+ /* Mark channel as used */
+ channels_assigned |= (1 << chan_nrs[channel_assigner]);
+
+ /* Save channel for use later in this call: */
+ chan = channel_assigner;
+ /* Round-ropin iterate channel assigner: */
+ channel_assigner = (channel_assigner + 1) % CHANNELS_NR;
+ }
+ }
+#if 0
+ fprintf(stderr, " --> %d [%04x], {%d,%d,%d}@%d\n", chan,
+ channels_assigned, chan_nrs[0],chan_nrs[1],chan_nrs[2],channel_assigner);
+#endif
+
+ switch (command & 0xf0) {
+
+ case 0x80:
+ if (argv[0] == notes[chan])
+ notes[chan] = 0;
+ break;
+
+ case 0x90:
+ if (!argv[1]) {
+ if (argv[chan] == notes[chan])
+ notes[chan] = 0;
+ } else {
+ notes[chan] = argv[0];
+ volumes[chan] = argv[1];
+ }
+ break;
+
+ case 0xb0:
+ if (argv[1] == SCI_MIDI_CHANNEL_NOTES_OFF)
+ notes[chan] = 0;
+ break;
+
+
+ default:
+#if DEBUG
+ fprintf(stderr, "[SFX:PCM-PC] Unused MIDI command %02x %02x %02x\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0);
+#endif
+ break; /* ignore */
+ }
+}
+
+#define BASE_NOTE 129 /* A10 */
+#define BASE_OCTAVE 10 /* A10, as I said */
+
+static int
+freq_table[12] = { /* A4 is 440Hz, halftone map is x |-> ** 2^(x/12) */
+ 28160, /* A10 */
+ 29834,
+ 31608,
+ 33488,
+ 35479,
+ 37589,
+ 39824,
+ 42192,
+ 44701,
+ 47359,
+ 50175,
+ 53159
+};
+
+static inline int
+get_freq(int note)
+{
+ int halftone_delta = note - BASE_NOTE;
+ int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE;
+ int halftone_index = (halftone_delta + (12*100)) % 12 ;
+ int freq = (!note)? 0 : freq_table[halftone_index] / (1 << (-oct_diff));
+
+ return freq;
+}
+
+
+void
+SN76496_poll(sfx_softseq_t *self, byte *dest, int len)
+{
+ gint16 *buf = (gint16 *) dest;
+ int i;
+ int chan;
+ int freq[CHANNELS_NR];
+
+ for (chan = 0; chan < CHANNELS_NR; chan++)
+ freq[chan] = get_freq(notes[chan]);
+
+ for (i = 0; i < len; i++) {
+ int result = 0;
+
+ for (chan = 0; chan < CHANNELS_NR; chan++)
+ if (notes[chan]) {
+ int volume = (global_volume * volumes[chan])
+ >> VOLUME_SHIFT;
+
+ freq_count[chan] += freq[chan];
+ while (freq_count[chan] >= (FREQUENCY << 1))
+ freq_count[chan] -= (FREQUENCY << 1);
+
+ if (freq_count[chan] - freq[chan] < 0) {
+ /* Unclean rising edge */
+ int l = volume << 1;
+ result += -volume + (l*freq_count[chan])/freq[chan];
+ } else if (freq_count[chan] >= FREQUENCY
+ && freq_count[chan] - freq[chan] < FREQUENCY) {
+ /* Unclean falling edge */
+ int l = volume << 1;
+ result += volume - (l*(freq_count[chan] - FREQUENCY))/freq[chan];
+ } else {
+ if (freq_count[chan] < FREQUENCY)
+ result += volume;
+ else
+ result += -volume;
+ }
+ }
+ buf[i] = result;
+ }
+
+}
+
+void
+SN76496_allstop(sfx_softseq_t *self)
+{
+ int i;
+ for (i = 0; i < CHANNELS_NR; i++)
+ notes[i] = 0;
+}
+
+void
+SN76496_volume(sfx_softseq_t *self, int new_volume)
+{
+ global_volume = new_volume;
+}
+
+
+sfx_softseq_t sfx_softseq_SN76496 = {
+ "SN76496",
+ "0.1",
+ SN76496_set_option,
+ SN76496_init,
+ SN76496_exit,
+ SN76496_volume,
+ SN76496_event,
+ SN76496_poll,
+ SN76496_allstop,
+ NULL,
+ SFX_SEQ_PATCHFILE_NONE,
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x10, /* Tandy/PCJr channels */
+ 0, /* No rhythm channel */
+ 3, /* # of voices */
+ {FREQUENCY, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/amiga.c b/engines/sci/sfx/softseq/amiga.c
new file mode 100644
index 0000000000..12240897d7
--- /dev/null
+++ b/engines/sci/sfx/softseq/amiga.c
@@ -0,0 +1,658 @@
+/***************************************************************************
+ amiga.c Copyright (C) 2007 Walter van Niftrik
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+#include "resource.h"
+#include "sci_memory.h"
+#include "../softseq.h"
+
+#define FREQUENCY 44100
+#define CHANNELS_NR 10
+#define HW_CHANNELS_NR 16
+
+/* Samplerate of the instrument bank */
+#define BASE_FREQ 20000
+
+/* Instrument looping flag */
+#define MODE_LOOP 1 << 0
+/* Instrument pitch changes flag */
+#define MODE_PITCH 1 << 1
+
+#define PAN_LEFT 91
+#define PAN_RIGHT 164
+
+/* #define DEBUG */
+
+typedef struct envelope {
+ /* Phase period length in samples */
+ int length;
+ /* Velocity delta per period */
+ int delta;
+ /* Target velocity */
+ int target;
+} envelope_t;
+
+/* Fast decay envelope */
+static envelope_t env_decay = {FREQUENCY / (32 * 64), 1, 0};
+
+typedef struct instrument {
+ char name[30];
+ int mode;
+ /* Size of non-looping part in bytes */
+ int size;
+ /* Starting offset and size of loop in bytes */
+ int loop_size;
+ /* Transpose value in semitones */
+ int transpose;
+ /* Envelope */
+ envelope_t envelope[4];
+ sbyte *samples;
+ sbyte *loop;
+} instrument_t;
+
+typedef struct bank {
+ char name[30];
+ int size;
+ instrument_t *instruments[256];
+} bank_t;
+
+typedef struct channel {
+ int instrument;
+ int note;
+ int note_velocity;
+ int velocity;
+ int envelope;
+ /* Number of samples till next envelope event */
+ int envelope_samples;
+ int decay;
+ int looping;
+ int hw_channel;
+ frac_t offset;
+ frac_t rate;
+} channel_t;
+
+typedef struct hw_channel {
+ int instrument;
+ int volume;
+ int pan;
+} hw_channel_t;
+
+/* Instrument bank */
+static bank_t bank;
+/* Internal channels */
+static channel_t channels[CHANNELS_NR];
+/* External channels */
+static hw_channel_t hw_channels[HW_CHANNELS_NR];
+/* Overall volume */
+static int volume = 127;
+
+/* Frequencies for every note */
+static int freq_table[] = {
+ 58, 62, 65, 69, 73, 78, 82, 87,
+ 92, 98, 104, 110, 117, 124, 131, 139,
+ 147, 156, 165, 175, 185, 196, 208, 220,
+ 234, 248, 262, 278, 294, 312, 331, 350,
+ 371, 393, 417, 441, 468, 496, 525, 556,
+ 589, 625, 662, 701, 743, 787, 834, 883,
+ 936, 992, 1051, 1113, 1179, 1250, 1324, 1403,
+ 1486, 1574, 1668, 1767, 1872, 1984, 2102, 2227,
+ 2359, 2500, 2648, 2806, 2973, 3149, 3337, 3535,
+ 3745, 3968, 4204, 4454, 4719, 5000, 5297, 5612,
+ 5946, 6299, 6674, 7071, 7491, 7937, 8408, 8908,
+ 9438, 10000, 10594, 11224, 11892, 12599, 13348, 14142,
+ 14983, 15874, 16817, 17817, 18877, 20000, 21189, 22449,
+ 23784, 25198, 26696, 28284, 29966, 31748, 33635, 35635,
+ 37754, 40000, 42378, 44898, 47568, 50396, 53393, 56568,
+ 59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796
+};
+
+static void
+set_envelope(channel_t *channel, envelope_t *envelope, int phase)
+{
+ channel->envelope = phase;
+ channel->envelope_samples = envelope[phase].length;
+
+ if (phase == 0)
+ channel->velocity = channel->note_velocity / 2;
+ else
+ channel->velocity = envelope[phase - 1].target;
+}
+
+static inline int
+interpolate(sbyte *samples, frac_t offset)
+{
+ int x = frac_to_int(offset);
+ int diff = (samples[x + 1] - samples[x]) << 8;
+
+ return (samples[x] << 8) + frac_to_int(diff * (offset & FRAC_LO_MASK));
+}
+
+static void
+play_instrument(gint16 *dest, channel_t *channel, int count)
+{
+ int index = 0;
+ int vol = hw_channels[channel->hw_channel].volume;
+ instrument_t *instrument = bank.instruments[channel->instrument];
+
+ while (1) {
+ /* Available source samples until end of segment */
+ frac_t lin_avail;
+ int seg_end, rem, i, amount;
+ sbyte *samples;
+
+ if (channel->looping) {
+ samples = instrument->loop;
+ seg_end = instrument->loop_size;
+ }
+ else {
+ samples = instrument->samples;
+ seg_end = instrument->size;
+ }
+
+ lin_avail = int_to_frac(seg_end) - channel->offset;
+
+ rem = count - index;
+
+ /* Amount of destination samples that we will compute this iteration */
+ amount = lin_avail / channel->rate;
+
+ if (lin_avail % channel->rate)
+ amount++;
+
+ if (amount > rem)
+ amount = rem;
+
+ /* Stop at next envelope event */
+ if ((channel->envelope_samples != -1) && (amount > channel->envelope_samples))
+ amount = channel->envelope_samples;
+
+ for (i = 0; i < amount; i++) {
+ dest[index++] = interpolate(samples, channel->offset) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127);
+ channel->offset += channel->rate;
+ }
+
+ if (channel->envelope_samples != -1)
+ channel->envelope_samples -= amount;
+
+ if (channel->envelope_samples == 0) {
+ envelope_t *envelope;
+ int delta, target, velocity;
+
+ if (channel->decay)
+ envelope = &env_decay;
+ else
+ envelope = &instrument->envelope[channel->envelope];
+
+ delta = envelope->delta;
+ target = envelope->target;
+ velocity = channel->velocity - envelope->delta;
+
+ /* Check whether we have reached the velocity target for the current phase */
+ if ((delta >= 0 && velocity <= target) || (delta < 0 && velocity >= target)) {
+ channel->velocity = target;
+
+ /* Stop note after velocity has dropped to 0 */
+ if (target == 0) {
+ channel->note = -1;
+ break;
+ } else
+ switch (channel->envelope) {
+ case 0:
+ case 2:
+ /* Go to next phase */
+ set_envelope(channel, instrument->envelope, channel->envelope + 1);
+ break;
+ case 1:
+ case 3:
+ /* Stop envelope */
+ channel->envelope_samples = -1;
+ break;
+ }
+ } else {
+ /* We haven't reached the target yet */
+ channel->envelope_samples = envelope->length;
+ channel->velocity = velocity;
+ }
+ }
+
+ if (index == count)
+ break;
+
+ if (frac_to_int(channel->offset) >= seg_end) {
+ if (instrument->mode & MODE_LOOP) {
+ /* Loop the samples */
+ channel->offset -= int_to_frac(seg_end);
+ channel->looping = 1;
+ } else {
+ /* All samples have been played */
+ channel->note = -1;
+ break;
+ }
+ }
+ }
+}
+
+static void
+change_instrument(int channel, int instrument)
+{
+#ifdef DEBUG
+ if (bank.instruments[instrument])
+ sciprintf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, bank.instruments[instrument]->name, instrument);
+ else
+ sciprintf("[sfx:seq:amiga] Warning: instrument %i does not exist (channel %i)\n", instrument, channel);
+#endif
+ hw_channels[channel].instrument = instrument;
+}
+
+static void
+stop_channel(int ch)
+{
+ int i;
+
+ /* Start decay phase for note on this hw channel, if any */
+ for (i = 0; i < CHANNELS_NR; i++)
+ if (channels[i].note != -1 && channels[i].hw_channel == ch && !channels[i].decay) {
+ /* Trigger fast decay envelope */
+ channels[i].decay = 1;
+ channels[i].envelope_samples = env_decay.length;
+ break;
+ }
+}
+
+static void
+stop_note(int ch, int note)
+{
+ int channel;
+ instrument_t *instrument;
+
+ for (channel = 0; channel < CHANNELS_NR; channel++)
+ if (channels[channel].note == note && channels[channel].hw_channel == ch && !channels[channel].decay)
+ break;
+
+ if (channel == CHANNELS_NR) {
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Warning: cannot stop note %i on channel %i\n", note, ch);
+#endif
+ return;
+ }
+
+ instrument = bank.instruments[channels[channel].instrument];
+
+ /* Start the envelope phases for note-off if looping is on and envelope is enabled */
+ if ((instrument->mode & MODE_LOOP) && (instrument->envelope[0].length != 0))
+ set_envelope(&channels[channel], instrument->envelope, 2);
+}
+
+static void
+start_note(int ch, int note, int velocity)
+{
+ instrument_t *instrument;
+ int channel;
+
+ if (hw_channels[ch].instrument < 0 || hw_channels[ch].instrument > 255) {
+ sciprintf("[sfx:seq:amiga] Error: invalid instrument %i on channel %i\n", hw_channels[ch].instrument, ch);
+ return;
+ }
+
+ instrument = bank.instruments[hw_channels[ch].instrument];
+
+ if (!instrument) {
+ sciprintf("[sfx:seq:amiga] Error: instrument %i does not exist\n", hw_channels[ch].instrument);
+ return;
+ }
+
+ for (channel = 0; channel < CHANNELS_NR; channel++)
+ if (channels[channel].note == -1)
+ break;
+
+ if (channel == CHANNELS_NR) {
+ sciprintf("[sfx:seq:amiga] Warning: could not find a free channel\n");
+ return;
+ }
+
+ stop_channel(ch);
+
+ if (instrument->mode & MODE_PITCH) {
+ int fnote = note + instrument->transpose;
+
+ if (fnote < 0 || fnote > 127) {
+ sciprintf("[sfx:seq:amiga] Error: illegal note %i\n", fnote);
+ return;
+ }
+
+ /* Compute rate for note */
+ channels[channel].rate = double_to_frac(freq_table[fnote] / (double) FREQUENCY);
+ }
+ else
+ channels[channel].rate = double_to_frac(BASE_FREQ / (double) FREQUENCY);
+
+ channels[channel].instrument = hw_channels[ch].instrument;
+ channels[channel].note = note;
+ channels[channel].note_velocity = velocity;
+
+ if ((instrument->mode & MODE_LOOP) && (instrument->envelope[0].length != 0))
+ set_envelope(&channels[channel], instrument->envelope, 0);
+ else {
+ channels[channel].velocity = 64;
+ channels[channel].envelope_samples = -1;
+ }
+
+ channels[channel].offset = 0;
+ channels[channel].hw_channel = ch;
+ channels[channel].decay = 0;
+ channels[channel].looping = 0;
+}
+
+static gint16 read_int16(byte *data)
+{
+ return (data[0] << 8) | data[1];
+}
+
+static gint32 read_int32(byte *data)
+{
+ return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+}
+
+static instrument_t *read_instrument(FILE *file, int *id)
+{
+ instrument_t *instrument;
+ byte header[61];
+ int size;
+ int seg_size[3];
+ int loop_offset;
+ int i;
+
+ if (fread(header, 1, 61, file) < 61) {
+ sciprintf("[sfx:seq:amiga] Error: failed to read instrument header\n");
+ return NULL;
+ }
+
+ instrument = (instrument_t *) sci_malloc(sizeof(instrument_t));
+
+ seg_size[0] = read_int16(header + 35) * 2;
+ seg_size[1] = read_int16(header + 41) * 2;
+ seg_size[2] = read_int16(header + 47) * 2;
+
+ instrument->mode = header[33];
+ instrument->transpose = (gint8) header[34];
+ for (i = 0; i < 4; i++) {
+ int length = (gint8) header[49 + i];
+
+ if (length == 0 && i > 0)
+ length = 256;
+
+ instrument->envelope[i].length = length * FREQUENCY / 60;
+ instrument->envelope[i].delta = (gint8) header[53 + i];
+ instrument->envelope[i].target = header[57 + i];
+ }
+ /* Final target must be 0 */
+ instrument->envelope[3].target = 0;
+
+ loop_offset = read_int32(header + 37) & ~1;
+ size = seg_size[0] + seg_size[1] + seg_size[2];
+
+ *id = read_int16(header);
+
+ strncpy(instrument->name, (char *) header + 2, 29);
+ instrument->name[29] = 0;
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
+ *id, instrument->name, size);
+ sciprintf(" Mode: %02x\n", instrument->mode);
+ sciprintf(" Looping: %s\n", instrument->mode & MODE_LOOP ? "on" : "off");
+ sciprintf(" Pitch changes: %s\n", instrument->mode & MODE_PITCH ? "on" : "off");
+ sciprintf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]);
+ sciprintf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43));
+#endif
+ instrument->samples = (sbyte *) sci_malloc(size + 1);
+ if (fread(instrument->samples, 1, size, file) < size) {
+ sciprintf("[sfx:seq:amiga] Error: failed to read instrument samples\n");
+ return NULL;
+ }
+
+ if (instrument->mode & MODE_LOOP) {
+ if (loop_offset + seg_size[1] > size) {
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Warning: looping samples extend %i bytes past end of sample block\n",
+ loop_offset + seg_size[1] - size);
+#endif
+ seg_size[1] = size - loop_offset;
+ }
+
+ if (seg_size[1] < 0) {
+ sciprintf("[sfx:seq:amiga] Error: invalid looping point\n");
+ return NULL;
+ }
+
+ instrument->size = seg_size[0];
+ instrument->loop_size = seg_size[1];
+
+ instrument->loop = (sbyte*)sci_malloc(instrument->loop_size + 1);
+ memcpy(instrument->loop, instrument->samples + loop_offset, instrument->loop_size);
+
+ instrument->samples[instrument->size] = instrument->loop[0];
+ instrument->loop[instrument->loop_size] = instrument->loop[0];
+ } else {
+ instrument->size = size;
+ instrument->samples[instrument->size] = 0;
+ }
+
+ return instrument;
+}
+
+static int
+ami_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+ami_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2, int patch2_len)
+{
+ FILE *file;
+ byte header[40];
+ int i;
+
+ file = sci_fopen("bank.001", "rb");
+
+ if (!file) {
+ sciprintf("[sfx:seq:amiga] Error: file bank.001 not found\n");
+ return SFX_ERROR;
+ }
+
+ if (fread(header, 1, 40, file) < 40) {
+ sciprintf("[sfx:seq:amiga] Error: failed to read header of file bank.001\n");
+ fclose(file);
+ return SFX_ERROR;
+ }
+
+ for (i = 0; i < 256; i++)
+ bank.instruments[i] = NULL;
+
+ for (i = 0; i < CHANNELS_NR; i++) {
+ channels[i].note = -1;
+ }
+
+ for (i = 0; i < HW_CHANNELS_NR; i++) {
+ hw_channels[i].instrument = -1;
+ hw_channels[i].volume = 127;
+ hw_channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? PAN_LEFT : PAN_RIGHT);
+ }
+
+ bank.size = read_int16(header + 38);
+ strncpy(bank.name, (char *) header + 8, 29);
+ bank.name[29] = 0;
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", bank.size, bank.name);
+#endif
+
+ for (i = 0; i < bank.size; i++) {
+ int id;
+ instrument_t *instrument = read_instrument(file, &id);
+
+ if (!instrument) {
+ sciprintf("[sfx:seq:amiga] Error: failed to read bank.001\n");
+ fclose(file);
+ return SFX_ERROR;
+ }
+
+ if (id < 0 || id > 255) {
+ sciprintf("[sfx:seq:amiga] Error: instrument ID out of bounds\n");
+ return SFX_ERROR;
+ }
+
+ bank.instruments[id] = instrument;
+ }
+
+ fclose(file);
+
+ return SFX_OK;
+}
+
+static void
+ami_exit(sfx_softseq_t *self)
+{
+ int i;
+
+ for (i = 0; i < bank.size; i++) {
+ if (bank.instruments[i]) {
+ sci_free(bank.instruments[i]->samples);
+ sci_free(bank.instruments[i]);
+ }
+ }
+}
+
+static void
+ami_event(sfx_softseq_t *self, byte command, int argc, byte *argv)
+{
+ int channel, oper;
+
+ channel = command & 0x0f;
+ oper = command & 0xf0;
+
+ if (channel >= HW_CHANNELS_NR) {
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Warning: received event for non-existing channel %i\n", channel);
+#endif
+ return;
+ }
+
+ switch(oper) {
+ case 0x90:
+ if (argv[1] > 0)
+ start_note(channel, argv[0], argv[1]);
+ else
+ stop_note(channel, argv[0]);
+ break;
+ case 0xb0:
+ switch (argv[0]) {
+ case 0x07:
+ hw_channels[channel].volume = argv[1];
+ break;
+ case 0x0a:
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Warning: ignoring pan 0x%02x event for channel %i\n", argv[1], channel);
+#endif
+ break;
+ case 0x7b:
+ stop_channel(channel);
+ break;
+ default:
+ sciprintf("[sfx:seq:amiga] Warning: unknown control event 0x%02x\n", argv[0]);
+ }
+ break;
+ case 0xc0:
+ change_instrument(channel, argv[0]);
+ break;
+ default:
+ sciprintf("[sfx:seq:amiga] Warning: unknown event %02x\n", command);
+ }
+}
+
+void
+ami_poll(sfx_softseq_t *self, byte *dest, int len)
+{
+ int i, j;
+ gint16 *buf = (gint16 *) dest;
+ gint16 *buffers = (gint16*)malloc(len * 2 * CHANNELS_NR);
+
+ memset(buffers, 0, len * 2 * CHANNELS_NR);
+ memset(dest, 0, len * 4);
+
+ /* Generate samples for all notes */
+ for (i = 0; i < CHANNELS_NR; i++)
+ if (channels[i].note >= 0)
+ play_instrument(buffers + i * len, &channels[i], len);
+
+ for (j = 0; j < len; j++) {
+ int mixedl = 0, mixedr = 0;
+
+ /* Mix and pan */
+ for (i = 0; i < CHANNELS_NR; i++) {
+ mixedl += buffers[i * len + j] * (256 - hw_channels[channels[i].hw_channel].pan);
+ mixedr += buffers[i * len + j] * hw_channels[channels[i].hw_channel].pan;
+ }
+
+ /* Adjust volume */
+ buf[2 * j] = mixedl * volume >> 16;
+ buf[2 * j + 1] = mixedr *volume >> 16;
+ }
+}
+
+void
+ami_volume(sfx_softseq_t *self, int new_volume)
+{
+ volume = new_volume;
+}
+
+void
+ami_allstop(sfx_softseq_t *self)
+{
+ int i;
+ for (i = 0; i < HW_CHANNELS_NR; i++)
+ stop_channel(i);
+}
+
+sfx_softseq_t sfx_softseq_amiga = {
+ "amiga",
+ "0.1",
+ ami_set_option,
+ ami_init,
+ ami_exit,
+ ami_volume,
+ ami_event,
+ ami_poll,
+ ami_allstop,
+ NULL,
+ SFX_SEQ_PATCHFILE_NONE,
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x40,
+ 0, /* No rhythm channel (9) */
+ HW_CHANNELS_NR, /* # of voices */
+ {FREQUENCY, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/fluidsynth.c b/engines/sci/sfx/softseq/fluidsynth.c
new file mode 100644
index 0000000000..a44d75e5bd
--- /dev/null
+++ b/engines/sci/sfx/softseq/fluidsynth.c
@@ -0,0 +1,262 @@
+/***************************************************************************
+ fluidsynth.c Copyright (C) 2008 Walter van Niftrik
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_FLUIDSYNTH_H
+
+#include <fluidsynth.h>
+
+#include "../softseq.h"
+#include "../sequencer.h"
+#include "../device.h"
+#include "resource.h"
+
+static sfx_sequencer_t *gmseq;
+static fluid_settings_t* settings;
+static fluid_synth_t* synth;
+static guint8 status;
+static char *soundfont = "/etc/midi/8MBGMSFX.SF2";
+static int rpn[16];
+
+#define SAMPLE_RATE 44100
+#define CHANNELS SFX_PCM_STEREO_LR
+
+/* MIDI writer */
+
+static int
+fluidsynth_midi_init(struct _midi_writer *self)
+{
+ return SFX_OK;
+}
+
+static int
+fluidsynth_midi_set_option(struct _midi_writer *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+fluidsynth_midi_write(struct _midi_writer *self, unsigned char *buf, int len)
+{
+ if (buf[0] == 0xf0)
+ sciprintf("FluidSynth: Skipping sysex message.\n");
+ else if (len == 2) {
+ guint8 command, channel;
+
+ command = buf[0] & 0xf0;
+ channel = buf[0] & 0x0f;
+
+ switch(command) {
+ case 0xc0:
+ fluid_synth_program_change(synth, channel, buf[1]);
+ break;
+ default:
+ printf("FluidSynth: MIDI command [%02x %02x] not supported\n", buf[0], buf[1]);
+ }
+ }
+ else if (len == 3) {
+ guint8 command, channel;
+
+ command = buf[0] & 0xf0;
+ channel = buf[0] & 0x0f;
+
+ switch(command) {
+ case 0x80:
+ fluid_synth_noteoff(synth, channel, buf[1]);
+ break;
+ case 0x90:
+ fluid_synth_noteon(synth, channel, buf[1], buf[2]);
+ break;
+ case 0xb0:
+ switch (buf[1]) {
+ case 0x06:
+ /* Data Entry Slider - course */
+ if (rpn[channel] == 0)
+ fluid_synth_pitch_wheel_sens(synth, channel, buf[2]);
+ else
+ sciprintf("FluidSynth: RPN %i not supported\n", rpn[channel]);
+ case 0x64:
+ /* Registered Parameter Number (RPN) - fine */
+ rpn[channel] &= ~0x7f;
+ rpn[channel] |= buf[2] & 0x7f;
+ break;
+ case 0x65:
+ /* Registered Parameter Number (RPN) - course */
+ rpn[channel] &= ~0x3f80;
+ rpn[channel] |= (buf[2] & 0x7f) << 7;
+ break;
+ default:
+ fluid_synth_cc(synth, channel, buf[1], buf[2]);
+ }
+ break;
+ case 0xe0:
+ fluid_synth_pitch_bend(synth, channel, (buf[2] << 7) | buf[1]);
+ break;
+ default:
+ sciprintf("FluidSynth: MIDI command [%02x %02x %02x] not supported\n", buf[0], buf[1], buf[2]);
+ }
+ }
+ else
+ sciprintf("FluidSynth: Skipping invalid message of %i bytes.\n", len);
+
+ return SFX_OK;
+}
+
+static void
+fluidsynth_midi_delay(struct _midi_writer *self, int ticks)
+{
+}
+
+static void
+fluidsynth_midi_reset_timer(struct _midi_writer *self)
+{
+}
+
+static void
+fluidsynth_midi_close(struct _midi_writer *self)
+{
+}
+
+static midi_writer_t midi_writer_fluidsynth = {
+ "fluidsynth",
+ fluidsynth_midi_init,
+ fluidsynth_midi_set_option,
+ fluidsynth_midi_write,
+ fluidsynth_midi_delay,
+ NULL,
+ fluidsynth_midi_reset_timer,
+ fluidsynth_midi_close
+};
+
+/* Software sequencer */
+
+static void
+fluidsynth_poll(sfx_softseq_t *self, byte *dest, int count)
+{
+ fluid_synth_write_s16(synth, count, dest, 0, 2, dest + 2, 0, 2);
+}
+
+static int
+fluidsynth_init(sfx_softseq_t *self, byte *data_ptr, int data_length,
+ byte *data2_ptr, int data2_length)
+{
+ int sfont_id;
+ double min, max;
+
+ if (0) {
+ sciprintf("FluidSynth ERROR: Mono sound output not supported.\n");
+ return SFX_ERROR;
+ }
+
+ gmseq = sfx_find_sequencer("General MIDI");
+ if (!gmseq) {
+ sciprintf("FluidSynth ERROR: Unable to find General MIDI sequencer.\n");
+ return SFX_ERROR;
+ }
+
+ settings = new_fluid_settings();
+
+ fluid_settings_getnum_range(settings, "synth.sample-rate", &min, &max);
+ if (SAMPLE_RATE < min || SAMPLE_RATE > max) {
+ sciprintf("FluidSynth ERROR: Sample rate '%i' not supported. Valid "
+ "range is (%i-%i).\n", SAMPLE_RATE, (int) min, (int) max);
+ delete_fluid_settings(settings);
+ return SFX_ERROR;
+ }
+
+ fluid_settings_setnum(settings, "synth.sample-rate", SAMPLE_RATE);
+ fluid_settings_setnum(settings, "synth.gain", 0.5f);
+
+ synth = new_fluid_synth(settings);
+
+ if ((sfont_id = fluid_synth_sfload(synth, soundfont, 1)) < 0) {
+ delete_fluid_synth(synth);
+ delete_fluid_settings(settings);
+ return SFX_ERROR;
+ }
+
+ gmseq->open(data_length, data_ptr, data2_length, data2_ptr,
+ &midi_writer_fluidsynth);
+
+ return SFX_OK;
+}
+
+static void
+fluidsynth_exit(sfx_softseq_t *self)
+{
+ delete_fluid_synth(synth);
+ delete_fluid_settings(settings);
+}
+
+static void
+fluidsynth_allstop(sfx_softseq_t *self)
+{
+ if (gmseq->allstop)
+ gmseq->allstop();
+}
+
+static void
+fluidsynth_volume(sfx_softseq_t *self, int volume)
+{
+ if (gmseq->volume)
+ gmseq->volume(volume);
+}
+
+static int
+fluidsynth_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static void
+fluidsynth_event(sfx_softseq_t *self, byte cmd, int argc, byte *argv)
+{
+ gmseq->event(cmd, argc, argv);
+}
+
+sfx_softseq_t sfx_softseq_fluidsynth = {
+ "fluidsynth",
+ "0.1",
+ fluidsynth_set_option,
+ fluidsynth_init,
+ fluidsynth_exit,
+ fluidsynth_volume,
+ fluidsynth_event,
+ fluidsynth_poll,
+ fluidsynth_allstop,
+ NULL,
+ 004, /* patch.004 */
+ 001, /* patch.001 */
+ 0x01, /* playflag */
+ 1, /* do play channel 9 */
+ 32, /* Max polypgony */
+ {SAMPLE_RATE, CHANNELS, SFX_PCM_FORMAT_S16_NATIVE}
+};
+
+#endif /* HAVE_FLUIDSYNTH_H */
diff --git a/engines/sci/sfx/softseq/fmopl.c b/engines/sci/sfx/softseq/fmopl.c
new file mode 100644
index 0000000000..6ea777c667
--- /dev/null
+++ b/engines/sci/sfx/softseq/fmopl.c
@@ -0,0 +1,1144 @@
+/***************************************************************************
+ fmopl.c Copyright (C) 1999-2000 Tatsuyuki Satoh
+ 2001-2004 The ScummVM project
+ 2002 Solomon Peachy
+ 2004 Walter van Niftrik
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+ LGPL licensed version of MAMEs fmopl (V0.37a modified) by
+ Tatsuyuki Satoh. Included from LGPL'ed AdPlug.
+
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef INLINE
+# ifdef _MSC_VER
+# define INLINE __inline
+# else
+# define INLINE inline
+# endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+
+#include "fmopl.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */
+#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define FREQ_BITS 24 /* frequency turn */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE (1<<(FREQ_BITS-20))
+#define TL_BITS (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+int ENV_BITS;
+/* envelope output entries */
+int EG_ENT;
+
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static memory = EG_ENT*4 (byte) */
+int EG_OFF; /* OFF */
+int EG_DED;
+int EG_DST; /* DECAY START */
+int EG_AED;
+#define EG_AST 0 /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR 0x00
+#define ENV_MOD_DR 0x01
+#define ENV_MOD_AR 0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+#ifdef TWELVE_VOICE
+ 18,20,22,19,21,23,-1,-1
+#else
+ -1,-1,-1,-1,-1,-1,-1,-1
+#endif
+};
+
+static guint32 KSL_TABLE[8 * 16];
+
+static const double KSL_TABLE_SEED[8 * 16] = {
+ /* OCT 0 */
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.000, 0.000, 0.000,
+ /* OCT 1 */
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.750, 1.125, 1.500,
+ 1.875, 2.250, 2.625, 3.000,
+ /* OCT 2 */
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 1.125, 1.875, 2.625,
+ 3.000, 3.750, 4.125, 4.500,
+ 4.875, 5.250, 5.625, 6.000,
+ /* OCT 3 */
+ 0.000, 0.000, 0.000, 1.875,
+ 3.000, 4.125, 4.875, 5.625,
+ 6.000, 6.750, 7.125, 7.500,
+ 7.875, 8.250, 8.625, 9.000,
+ /* OCT 4 */
+ 0.000, 0.000, 3.000, 4.875,
+ 6.000, 7.125, 7.875, 8.625,
+ 9.000, 9.750, 10.125, 10.500,
+ 10.875, 11.250, 11.625, 12.000,
+ /* OCT 5 */
+ 0.000, 3.000, 6.000, 7.875,
+ 9.000, 10.125, 10.875, 11.625,
+ 12.000, 12.750, 13.125, 13.500,
+ 13.875, 14.250, 14.625, 15.000,
+ /* OCT 6 */
+ 0.000, 6.000, 9.000, 10.875,
+ 12.000, 13.125, 13.875, 14.625,
+ 15.000, 15.750, 16.125, 16.500,
+ 16.875, 17.250, 17.625, 18.000,
+ /* OCT 7 */
+ 0.000, 9.000, 12.000, 13.875,
+ 15.000, 16.125, 16.875, 17.625,
+ 18.000, 18.750, 19.125, 19.500,
+ 19.875, 20.250, 20.625, 21.000
+};
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+
+static int SL_TABLE[16];
+
+static const guint32 SL_TABLE_SEED[16] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31
+};
+
+#define TL_MAX (EG_ENT * 2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
+/* TL_TABLE[ 0 to TL_MAX ] : plus section */
+/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static int *TL_TABLE;
+
+/* pointers to TL_TABLE with sinwave output offset */
+static int **SIN_TABLE;
+
+/* LFO table */
+static int *AMS_TABLE;
+static int *VIB_TABLE;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+//static int ENV_CURVE[2*EG_ENT+1];
+static int ENV_CURVE[2 * 4096 + 1]; // to keep it static ...
+
+/* multiple table */
+#define ML(a) (int)(a * 2)
+static const guint32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+ ML(0.50), ML(1.00), ML(2.00), ML(3.00), ML(4.00), ML(5.00), ML(6.00), ML(7.00),
+ ML(8.00), ML(9.00), ML(10.00), ML(10.00),ML(12.00),ML(12.00),ML(15.00),ML(15.00)
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static int RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL; /* current chip point */
+/* currenct chip state */
+/* static OPLSAMPLE *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2;
+
+static int outd[1];
+static int ams;
+static int vib;
+int *ams_table;
+int *vib_table;
+static int amsIncr;
+static int vibIncr;
+static int feedback2; /* connect for SLOT 2 */
+
+/* --------------------- rebuild tables ------------------- */
+
+#define SC_KSL(mydb) ((guint32) (mydb / (EG_STEP / 2)))
+#define SC_SL(db) (int)(db * ((3 / EG_STEP) * (1 << ENV_BITS))) + EG_DST
+
+void OPLBuildTables(int ENV_BITS_PARAM, int EG_ENT_PARAM) {
+ int i;
+
+ ENV_BITS = ENV_BITS_PARAM;
+ EG_ENT = EG_ENT_PARAM;
+ EG_OFF = ((2 * EG_ENT)<<ENV_BITS); /* OFF */
+ EG_DED = EG_OFF;
+ EG_DST = (EG_ENT << ENV_BITS); /* DECAY START */
+ EG_AED = EG_DST;
+ //EG_STEP = (96.0/EG_ENT);
+
+ for (i = 0; i < (int)(sizeof(KSL_TABLE_SEED) / sizeof(double)); i++)
+ KSL_TABLE[i] = SC_KSL(KSL_TABLE_SEED[i]);
+
+ for (i = 0; i < (int)(sizeof(SL_TABLE_SEED) / sizeof(guint32)); i++)
+ SL_TABLE[i] = SC_SL(SL_TABLE_SEED[i]);
+}
+
+#undef SC_KSL
+#undef SC_SL
+
+/* --------------------- subroutines --------------------- */
+
+INLINE int Limit(int val, int max, int min) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL, int flag) {
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80)) {
+ if(OPL->status & OPL->statusmask) { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler)
+ (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL, int flag) {
+ /* reset status flag */
+ OPL->status &= ~flag;
+ if((OPL->status & 0x80)) {
+ if (!(OPL->status & OPL->statusmask)) {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL, int flag) {
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT) {
+ /* sin wave restart */
+ SLOT->Cnt = 0;
+ /* set attack */
+ SLOT->evm = ENV_MOD_AR;
+ SLOT->evs = SLOT->evsa;
+ SLOT->evc = EG_AST;
+ SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) {
+ if( SLOT->evm > ENV_MOD_RR) {
+ /* set envelope counter from envleope output */
+ SLOT->evm = ENV_MOD_RR;
+ if( !(SLOT->evc & EG_DST) )
+ //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
+ SLOT->evc = EG_DST;
+ SLOT->eve = EG_DED;
+ SLOT->evs = SLOT->evsr;
+ }
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE guint32 OPL_CALC_SLOT(OPL_SLOT *SLOT) {
+ /* calcrate envelope generator */
+ if((SLOT->evc += SLOT->evs) >= SLOT->eve) {
+ switch( SLOT->evm ){
+ case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+ /* next DR */
+ SLOT->evm = ENV_MOD_DR;
+ SLOT->evc = EG_DST;
+ SLOT->eve = SLOT->SL;
+ SLOT->evs = SLOT->evsd;
+ break;
+ case ENV_MOD_DR: /* DECAY -> SL or RR */
+ SLOT->evc = SLOT->SL;
+ SLOT->eve = EG_DED;
+ if(SLOT->eg_typ) {
+ SLOT->evs = 0;
+ } else {
+ SLOT->evm = ENV_MOD_RR;
+ SLOT->evs = SLOT->evsr;
+ }
+ break;
+ case ENV_MOD_RR: /* RR -> OFF */
+ SLOT->evc = EG_OFF;
+ SLOT->eve = EG_OFF + 1;
+ SLOT->evs = 0;
+ break;
+ }
+ }
+ /* calcrate envelope */
+ return SLOT->TLL + ENV_CURVE[SLOT->evc>>ENV_BITS] + (SLOT->ams ? ams : 0);
+}
+
+/* set algorythm connection */
+static void set_algorythm(OPL_CH *CH) {
+ int *carrier = &outd[0];
+ CH->connect1 = CH->CON ? carrier : &feedback2;
+ CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH, OPL_SLOT *SLOT) {
+ int ksr;
+
+ /* frequency step counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+ /* attack , decay rate recalcration */
+ SLOT->evsa = SLOT->AR[ksr];
+ SLOT->evsd = SLOT->DR[ksr];
+ SLOT->evsr = SLOT->RR[ksr];
+ }
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL, int slot, int v) {
+ OPL_CH *CH = &OPL->P_CH[slot / 2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot & 1];
+
+ SLOT->mul = MUL_TABLE[v & 0x0f];
+ SLOT->KSR = (v & 0x10) ? 0 : 2;
+ SLOT->eg_typ = (v & 0x20) >> 5;
+ SLOT->vib = (v & 0x40);
+ SLOT->ams = (v & 0x80);
+ CALC_FCSLOT(CH, SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL, int slot, int v) {
+ OPL_CH *CH = &OPL->P_CH[slot / 2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot & 1];
+ int ksl = v >> 6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (int)((v & 0x3f) * (0.75 / EG_STEP)); /* 0.75db step */
+
+ if(!(OPL->mode & 0x80)) { /* not CSM latch total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);
+ }
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL, int slot, int v) {
+ OPL_CH *CH = &OPL->P_CH[slot / 2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot & 1];
+ int ar = v >> 4;
+ int dr = v & 0x0f;
+
+ SLOT->AR = ar ? &OPL->AR_TABLE[ar << 2] : RATE_0;
+ SLOT->evsa = SLOT->AR[SLOT->ksr];
+ if(SLOT->evm == ENV_MOD_AR)
+ SLOT->evs = SLOT->evsa;
+
+ SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+ SLOT->evsd = SLOT->DR[SLOT->ksr];
+ if(SLOT->evm == ENV_MOD_DR)
+ SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL, int slot, int v) {
+ OPL_CH *CH = &OPL->P_CH[slot / 2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot & 1];
+ int sl = v >> 4;
+ int rr = v & 0x0f;
+
+ SLOT->SL = SL_TABLE[sl];
+ if(SLOT->evm == ENV_MOD_DR)
+ SLOT->eve = SLOT->SL;
+ SLOT->RR = &OPL->DR_TABLE[rr<<2];
+ SLOT->evsr = SLOT->RR[SLOT->ksr];
+ if(SLOT->evm == ENV_MOD_RR)
+ SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt + con) / (0x1000000 / SIN_ENT)) & (SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH(OPL_CH *CH) {
+ guint32 env_out;
+ OPL_SLOT *SLOT;
+
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if(env_out < (guint32)(EG_ENT - 1)) {
+ /* PG */
+ if(SLOT->vib)
+ SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE);
+ else
+ SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH->FB) {
+ int feedback1 = (CH->op1_out[0] + CH->op1_out[1]) >> CH->FB;
+ CH->op1_out[1] = CH->op1_out[0];
+ *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT, env_out, feedback1);
+ }
+ else {
+ *CH->connect1 += OP_OUT(SLOT, env_out, 0);
+ }
+ }else {
+ CH->op1_out[1] = CH->op1_out[0];
+ CH->op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH->SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if(env_out < (guint32)(EG_ENT - 1)) {
+ /* PG */
+ if(SLOT->vib)
+ SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE);
+ else
+ SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT, env_out, feedback2);
+ }
+}
+
+/* ---------- calcrate rythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH(OPL_CH *CH) {
+ guint32 env_tam, env_sd, env_top, env_hh;
+ int whitenoise = (int)((rand()&1) * (WHITE_NOISE_db / EG_STEP));
+ int tone8;
+
+ OPL_SLOT *SLOT;
+ int env_out;
+
+ /* BD : same as FM serial mode and output level is large */
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env_out = OPL_CALC_SLOT(SLOT);
+ if(env_out < EG_ENT-1) {
+ /* PG */
+ if(SLOT->vib)
+ SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE);
+ else
+ SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH[6].FB) {
+ int feedback1 = (CH[6].op1_out[0] + CH[6].op1_out[1]) >> CH[6].FB;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT, env_out, feedback1);
+ }
+ else {
+ feedback2 = OP_OUT(SLOT, env_out, 0);
+ }
+ }else {
+ feedback2 = 0;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ CH[6].op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH[6].SLOT[SLOT2];
+ env_out = OPL_CALC_SLOT(SLOT);
+ if(env_out < EG_ENT-1) {
+ /* PG */
+ if(SLOT->vib)
+ SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE);
+ else
+ SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT, env_out, feedback2) * 2;
+ }
+
+ // SD (17) = mul14[fnum7] + white noise
+ // TAM (15) = mul15[fnum8]
+ // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+ // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+ env_sd = OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+ env_tam =OPL_CALC_SLOT(SLOT8_1);
+ env_top = OPL_CALC_SLOT(SLOT8_2);
+ env_hh = OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+ /* PG */
+ if(SLOT7_1->vib)
+ SLOT7_1->Cnt += (2 * SLOT7_1->Incr * vib / VIB_RATE);
+ else
+ SLOT7_1->Cnt += 2 * SLOT7_1->Incr;
+ if(SLOT7_2->vib)
+ SLOT7_2->Cnt += ((CH[7].fc * 8) * vib / VIB_RATE);
+ else
+ SLOT7_2->Cnt += (CH[7].fc * 8);
+ if(SLOT8_1->vib)
+ SLOT8_1->Cnt += (SLOT8_1->Incr * vib / VIB_RATE);
+ else
+ SLOT8_1->Cnt += SLOT8_1->Incr;
+ if(SLOT8_2->vib)
+ SLOT8_2->Cnt += ((CH[8].fc * 48) * vib / VIB_RATE);
+ else
+ SLOT8_2->Cnt += (CH[8].fc * 48);
+
+ tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+ /* SD */
+ if(env_sd < (guint32)(EG_ENT - 1))
+ outd[0] += OP_OUT(SLOT7_1, env_sd, 0) * 8;
+ /* TAM */
+ if(env_tam < (guint32)(EG_ENT - 1))
+ outd[0] += OP_OUT(SLOT8_1, env_tam, 0) * 2;
+ /* TOP-CY */
+ if(env_top < (guint32)(EG_ENT - 1))
+ outd[0] += OP_OUT(SLOT7_2, env_top, tone8) * 2;
+ /* HH */
+ if(env_hh < (guint32)(EG_ENT-1))
+ outd[0] += OP_OUT(SLOT7_2, env_hh, tone8) * 2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables(FM_OPL *OPL, int ARRATE, int DRRATE) {
+ int i;
+ double rate;
+
+ /* make attack rate & decay rate tables */
+ for (i = 0; i < 4; i++)
+ OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+ for (i = 4; i <= 60; i++){
+ rate = OPL->freqbase; /* frequency rate */
+ if(i < 60)
+ rate *= 1.0 + (i & 3) * 0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+ rate *= 1 << ((i >> 2) - 1); /* b2-5 : shift bit */
+ rate *= (double)(EG_ENT << ENV_BITS);
+ OPL->AR_TABLE[i] = (int)(rate / ARRATE);
+ OPL->DR_TABLE[i] = (int)(rate / DRRATE);
+ }
+ for (i = 60; i < 75; i++) {
+ OPL->AR_TABLE[i] = EG_AED-1;
+ OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+ }
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable(void) {
+ int s,t;
+ double rate;
+ int i,j;
+ double pom;
+
+ /* allocate dynamic tables */
+ if((TL_TABLE = (int *)malloc(TL_MAX * 2 * sizeof(int))) == NULL)
+ return 0;
+ if((SIN_TABLE = (int **)malloc(SIN_ENT * 4 * sizeof(int *))) == NULL) {
+ free(TL_TABLE);
+ return 0;
+ }
+ if((AMS_TABLE = (int *)malloc(AMS_ENT * 2 * sizeof(int))) == NULL) {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ return 0;
+ }
+ if((VIB_TABLE = (int *)malloc(VIB_ENT * 2 * sizeof(int))) == NULL) {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ return 0;
+ }
+ /* make total level table */
+ for (t = 0; t < EG_ENT - 1 ; t++){
+ rate = ((1 << TL_BITS) - 1) / pow(10.0, EG_STEP * t / 20); /* dB -> voltage */
+ TL_TABLE[ t] = (int)rate;
+ TL_TABLE[TL_MAX + t] = -TL_TABLE[t];
+ }
+ /* fill volume off area */
+ for (t = EG_ENT - 1; t < TL_MAX; t++){
+ TL_TABLE[t] = TL_TABLE[TL_MAX + t] = 0;
+ }
+
+ /* make sinwave table (total level offet) */
+ /* degree 0 = degree 180 = off */
+ SIN_TABLE[0] = SIN_TABLE[SIN_ENT /2 ] = &TL_TABLE[EG_ENT - 1];
+ for (s = 1;s <= SIN_ENT / 4; s++){
+ pom = sin(2 * PI * s / SIN_ENT); /* sin */
+ pom = 20 * log10(1 / pom); /* decibel */
+ j = (int)(pom / EG_STEP); /* TL_TABLE steps */
+
+ /* degree 0 - 90 , degree 180 - 90 : plus section */
+ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT / 2 - s] = &TL_TABLE[j];
+ /* degree 180 - 270 , degree 360 - 270 : minus section */
+ SIN_TABLE[SIN_ENT / 2 + s] = SIN_TABLE[SIN_ENT - s] = &TL_TABLE[TL_MAX + j];
+ }
+ for (s = 0;s < SIN_ENT; s++) {
+ SIN_TABLE[SIN_ENT * 1 + s] = s < (SIN_ENT / 2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
+ SIN_TABLE[SIN_ENT * 2 + s] = SIN_TABLE[s % (SIN_ENT / 2)];
+ SIN_TABLE[SIN_ENT * 3 + s] = (s / (SIN_ENT / 4)) & 1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT * 2 + s];
+ }
+
+ /* envelope counter -> envelope output table */
+ for (i=0; i < EG_ENT; i++) {
+ /* ATTACK curve */
+ pom = pow(((double)(EG_ENT - 1 - i) / EG_ENT), 8) * EG_ENT;
+ /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+ ENV_CURVE[i] = (int)pom;
+ /* DECAY ,RELEASE curve */
+ ENV_CURVE[(EG_DST >> ENV_BITS) + i]= i;
+ }
+ /* off */
+ ENV_CURVE[EG_OFF >> ENV_BITS]= EG_ENT - 1;
+ /* make LFO ams table */
+ for (i=0; i < AMS_ENT; i++) {
+ pom = (1.0 + sin(2 * PI * i / AMS_ENT)) / 2; /* sin */
+ AMS_TABLE[i] = (int)((1.0 / EG_STEP) * pom); /* 1dB */
+ AMS_TABLE[AMS_ENT + i] = (int)((4.8 / EG_STEP) * pom); /* 4.8dB */
+ }
+ /* make LFO vibrate table */
+ for (i=0; i < VIB_ENT; i++) {
+ /* 100cent = 1seminote = 6% ?? */
+ pom = (double)VIB_RATE * 0.06 * sin(2 * PI * i / VIB_ENT); /* +-100sect step */
+ VIB_TABLE[i] = (int)(VIB_RATE + (pom * 0.07)); /* +- 7cent */
+ VIB_TABLE[VIB_ENT + i] = (int)(VIB_RATE + (pom * 0.14)); /* +-14cent */
+ }
+ return 1;
+}
+
+static void OPLCloseTable(void) {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ free(VIB_TABLE);
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH) {
+ OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+ OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+ /* all key off */
+ OPL_KEYOFF(slot1);
+ OPL_KEYOFF(slot2);
+ /* total level latch */
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ /* key on */
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(slot1);
+ OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initalize(FM_OPL *OPL) {
+ int fn;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0;
+ /* Timer base time */
+ OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+ /* make time tables */
+ init_timetables(OPL, OPL_ARRATE, OPL_DRRATE);
+ /* make fnumber -> increment counter table */
+ for( fn=0; fn < 1024; fn++) {
+ OPL->FN_TABLE[fn] = (guint32)(OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2);
+ }
+ /* LFO freq.table */
+ OPL->amsIncr = (int)(OPL->rate ? (double)AMS_ENT * (1 << AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0);
+ OPL->vibIncr = (int)(OPL->rate ? (double)VIB_ENT * (1 << VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0);
+}
+
+/* ---------- write a OPL registers ---------- */
+void OPLWriteReg(FM_OPL *OPL, int r, int v) {
+ OPL_CH *CH;
+ int slot;
+ guint32 block_fnum;
+
+ switch(r & 0xe0) {
+ case 0x00: /* 00-1f:controll */
+ switch(r & 0x1f) {
+ case 0x01:
+ /* wave selector enable */
+ if(OPL->type&OPL_TYPE_WAVESEL) {
+ OPL->wavesel = v & 0x20;
+ if(!OPL->wavesel) {
+ /* preset compatible mode */
+ int c;
+ for(c=0; c<OPL->max_ch; c++) {
+ OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
+ OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
+ }
+ }
+ }
+ return;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v) * 4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v) * 16;
+ return;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v & 0x80) { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL, 0x7f);
+ }
+ else { /* set IRQ mask ,timer enable*/
+ guint8 st1 = v & 1;
+ guint8 st2 = (v >> 1) & 1;
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL, v & 0x78);
+ OPL_STATUSMASK_SET(OPL,((~v) & 0x78) | 0x01);
+ /* timer 2 */
+ if(OPL->st[1] != st2) {
+ double interval = st2 ? (double)OPL->T[1] * OPL->TimerBase : 0.0;
+ OPL->st[1] = st2;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam + 1, interval);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1) {
+ double interval = st1 ? (double)OPL->T[0] * OPL->TimerBase : 0.0;
+ OPL->st[0] = st1;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam + 0, interval);
+ }
+ }
+ return;
+ }
+ break;
+ case 0x20: /* am,vib,ksr,eg type,mul */
+ slot = slot_array[r&0x1f];
+ if(slot == -1)
+ return;
+ set_mul(OPL,slot,v);
+ return;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot == -1)
+ return;
+ set_ksl_tl(OPL,slot,v);
+ return;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot == -1)
+ return;
+ set_ar_dr(OPL,slot,v);
+ return;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot == -1)
+ return;
+ set_sl_rr(OPL,slot,v);
+ return;
+ case 0xa0:
+ switch(r) {
+ case 0xbd:
+ /* amsep,vibdep,r,bd,sd,tom,tc,hh */
+ {
+ guint8 rkey = OPL->rythm ^ v;
+ OPL->ams_table = &AMS_TABLE[v & 0x80 ? AMS_ENT : 0];
+ OPL->vib_table = &VIB_TABLE[v & 0x40 ? VIB_ENT : 0];
+ OPL->rythm = v & 0x3f;
+ if(OPL->rythm & 0x20) {
+ /* BD key on/off */
+ if(rkey & 0x10) {
+ if(v & 0x10) {
+ OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ else {
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ }
+ /* SD key on/off */
+ if(rkey & 0x08) {
+ if(v & 0x08)
+ OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+ else
+ OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+ }/* TAM key on/off */
+ if(rkey & 0x04) {
+ if(v & 0x04)
+ OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+ else
+ OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+ }
+ /* TOP-CY key on/off */
+ if(rkey & 0x02) {
+ if(v & 0x02)
+ OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+ else
+ OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+ }
+ /* HH key on/off */
+ if(rkey & 0x01) {
+ if(v & 0x01)
+ OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+ else
+ OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+ }
+ }
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if((r & 0x0f) >= ADLIB_VOICES)
+ return;
+ CH = &OPL->P_CH[r & 0x0f];
+ if(!(r&0x10)) { /* a0-a8 */
+ block_fnum = (CH->block_fnum & 0x1f00) | v;
+ }
+ else { /* b0-b8 */
+ int keyon = (v >> 5) & 1;
+ block_fnum = ((v & 0x1f) << 8) | (CH->block_fnum & 0xff);
+ if(CH->keyon != keyon) {
+ if((CH->keyon=keyon)) {
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(&CH->SLOT[SLOT1]);
+ OPL_KEYON(&CH->SLOT[SLOT2]);
+ }
+ else {
+ OPL_KEYOFF(&CH->SLOT[SLOT1]);
+ OPL_KEYOFF(&CH->SLOT[SLOT2]);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum) {
+ int blockRv = 7 - (block_fnum >> 10);
+ int fnum = block_fnum & 0x3ff;
+ CH->block_fnum = block_fnum;
+ CH->ksl_base = KSL_TABLE[block_fnum >> 6];
+ CH->fc = OPL->FN_TABLE[fnum] >> blockRv;
+ CH->kcode = CH->block_fnum >> 9;
+ if((OPL->mode & 0x40) && CH->block_fnum & 0x100)
+ CH->kcode |=1;
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ return;
+ case 0xc0:
+ /* FB,C */
+ if((r & 0x0f) >= ADLIB_VOICES)
+ return;
+ CH = &OPL->P_CH[r&0x0f];
+ {
+ int feedback = (v >> 1) & 7;
+ CH->FB = feedback ? (8 + 1) - feedback : 0;
+ CH->CON = v & 1;
+ set_algorythm(CH);
+ }
+ return;
+ case 0xe0: /* wave type */
+ slot = slot_array[r & 0x1f];
+ if(slot == -1)
+ return;
+ CH = &OPL->P_CH[slot / 2];
+ if(OPL->wavesel) {
+ CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v & 0x03) * SIN_ENT];
+ }
+ return;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+ num_lock++;
+ if(num_lock>1)
+ return 0;
+ /* first time */
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if(!OPLOpenTable()) {
+ num_lock--;
+ return -1;
+ }
+ return 0;
+}
+
+static void OPL_UnLockTable(void) {
+ if(num_lock)
+ num_lock--;
+ if(num_lock)
+ return;
+ /* last time */
+ cur_chip = NULL;
+ OPLCloseTable();
+}
+
+/*******************************************************************************/
+/* YM3812 local section */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, gint16 *buffer, int length, int interleave) {
+ int i;
+ int data;
+ gint16 *buf = buffer;
+ guint32 amsCnt = OPL->amsCnt;
+ guint32 vibCnt = OPL->vibCnt;
+ guint8 rythm = OPL->rythm & 0x20;
+ OPL_CH *CH, *R_CH;
+
+ if((void *)OPL != cur_chip){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for(i = 0; i < length; i++) {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt += amsIncr) >> AMS_SHIFT];
+ vib = vib_table[(vibCnt += vibIncr) >> VIB_SHIFT];
+ outd[0] = 0;
+ /* FM part */
+ for(CH=S_CH; CH < R_CH; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+#ifdef TWELVE_VOICE
+ for(CH=&S_CH[9]; CH < &S_CH[ADLIB_VOICES]; CH++)
+ OPL_CALC_CH(CH);
+#endif
+ /* limit check */
+ data = Limit(outd[0], OPL_MAXOUT, OPL_MINOUT);
+ /* store to sound buffer */
+ buf[i << interleave] = data >> OPL_OUTSB;
+ }
+
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+}
+
+/* ---------- reset a chip ---------- */
+void OPLResetChip(FM_OPL *OPL) {
+ int c,s;
+ int i;
+
+ /* reset chip */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL, 0x7f);
+ /* reset with register write */
+ OPLWriteReg(OPL, 0x01,0); /* wabesel disable */
+ OPLWriteReg(OPL, 0x02,0); /* Timer1 */
+ OPLWriteReg(OPL, 0x03,0); /* Timer2 */
+ OPLWriteReg(OPL, 0x04,0); /* IRQ mask clear */
+ for(i = 0xff; i >= 0x20; i--)
+ OPLWriteReg(OPL,i,0);
+ /* reset OPerator parameter */
+ for(c = 0; c < OPL->max_ch ;c++ ) {
+ OPL_CH *CH = &OPL->P_CH[c];
+ /* OPL->P_CH[c].PAN = OPN_CENTER; */
+ for(s = 0; s < 2; s++ ) {
+ /* wave table */
+ CH->SLOT[s].wavetable = &SIN_TABLE[0];
+ /* CH->SLOT[s].evm = ENV_MOD_RR; */
+ CH->SLOT[s].evc = EG_OFF;
+ CH->SLOT[s].eve = EG_OFF + 1;
+ CH->SLOT[s].evs = 0;
+ }
+ }
+}
+
+/* ---------- Create a virtual YM3812 ---------- */
+/* 'rate' is sampling rate and 'bufsiz' is the size of the */
+FM_OPL *OPLCreate(int type, int clock, int rate) {
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+#ifdef TWELVE_VOICE
+ int max_ch = 12;
+#else
+ int max_ch = 9; /* normaly 9 channels */
+#endif
+
+ if( OPL_LockTable() == -1)
+ return NULL;
+ /* allocate OPL state space */
+ state_size = sizeof(FM_OPL);
+ state_size += sizeof(OPL_CH) * max_ch;
+
+ /* allocate memory block */
+ ptr = (char *)calloc(state_size, 1);
+ if(ptr == NULL)
+ return NULL;
+
+ /* clear */
+ memset(ptr, 0, state_size);
+ OPL = (FM_OPL *)ptr; ptr += sizeof(FM_OPL);
+ OPL->P_CH = (OPL_CH *)ptr; ptr += sizeof(OPL_CH) * max_ch;
+
+ /* set channel state pointer */
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+ OPL->max_ch = max_ch;
+
+ /* init grobal tables */
+ OPL_initalize(OPL);
+
+ /* reset chip */
+ OPLResetChip(OPL);
+ return OPL;
+}
+
+/* ---------- Destroy one of vietual YM3812 ---------- */
+void OPLDestroy(FM_OPL *OPL) {
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* ---------- Option handlers ---------- */
+
+void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler,int channelOffset) {
+ OPL->TimerHandler = TimerHandler;
+ OPL->TimerParam = channelOffset;
+}
+
+void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param) {
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler,int param) {
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+
+/* ---------- YM3812 I/O interface ---------- */
+int OPLWrite(FM_OPL *OPL,int a,int v) {
+ if(!(a & 1)) { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else { /* data port */
+ if(OPL->UpdateHandler)
+ OPL->UpdateHandler(OPL->UpdateParam,0);
+ OPLWriteReg(OPL, OPL->address,v);
+ }
+ return OPL->status >> 7;
+}
+
+unsigned char OPLRead(FM_OPL *OPL,int a) {
+ if(!(a & 1)) { /* status port */
+ return OPL->status & (OPL->statusmask | 0x80);
+ }
+ /* data port */
+ switch(OPL->address) {
+ case 0x05: /* KeyBoard IN */
+ fprintf(stderr, "OPL:read unmapped KEYBOARD port\n");
+ return 0;
+ case 0x19: /* I/O DATA */
+ fprintf(stderr, "OPL:read unmapped I/O port\n");
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ return 0;
+ }
+ return 0;
+}
+
+int OPLTimerOver(FM_OPL *OPL, int c) {
+ if(c) { /* Timer B */
+ OPL_STATUS_SET(OPL, 0x20);
+ }
+ else { /* Timer A */
+ OPL_STATUS_SET(OPL, 0x40);
+ /* CSM mode key,TL controll */
+ if(OPL->mode & 0x80) { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler)
+ OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch = 0; ch < 9; ch++)
+ CSMKeyControll(&OPL->P_CH[ch]);
+ }
+ }
+ /* reload timer */
+ if (OPL->TimerHandler)
+ (OPL->TimerHandler)(OPL->TimerParam + c, (double)OPL->T[c] * OPL->TimerBase);
+ return OPL->status >> 7;
+}
diff --git a/engines/sci/sfx/softseq/fmopl.h b/engines/sci/sfx/softseq/fmopl.h
new file mode 100644
index 0000000000..5bb1cd80d7
--- /dev/null
+++ b/engines/sci/sfx/softseq/fmopl.h
@@ -0,0 +1,168 @@
+/***************************************************************************
+ fmopl.h Copyright (C) 1999-2000 Tatsuyuki Satoh
+ 2001-2004 The ScummVM project
+ 2002 Solomon Peachy
+ 2004 Walter van Niftrik
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+ LGPL licensed version of MAMEs fmopl (V0.37a modified) by
+ Tatsuyuki Satoh. Included from LGPL'ed AdPlug.
+
+***************************************************************************/
+
+#ifndef FMOPL_H_
+#define FMOPL_H_
+
+#include <scitypes.h>
+
+#define TWELVE_VOICE 1
+#ifdef TWELVE_VOICE
+#define ADLIB_VOICES 12
+#else
+#define ADLIB_VOICES 9
+#endif
+
+enum {
+ FMOPL_ENV_BITS_HQ = 16,
+ FMOPL_ENV_BITS_LQ = 8,
+ FMOPL_EG_ENT_HQ = 4096,
+ FMOPL_EG_ENT_LQ = 128
+};
+
+
+typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
+typedef void (*OPL_IRQHANDLER)(int param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+
+/* Saving is necessary for member of the 'R' mark for suspend/resume */
+/* ---------- OPL one of slot ---------- */
+typedef struct fm_opl_slot {
+ int TL; /* total level :TL << 8 */
+ int TLL; /* adjusted now TL */
+ guint8 KSR; /* key scale rate :(shift down bit) */
+ int *AR; /* attack rate :&AR_TABLE[AR<<2] */
+ int *DR; /* decay rate :&DR_TABLE[DR<<2] */
+ int SL; /* sustain level :SL_TABLE[SL] */
+ int *RR; /* release rate :&DR_TABLE[RR<<2] */
+ guint8 ksl; /* keyscale level :(shift down bits) */
+ guint8 ksr; /* key scale rate :kcode>>KSR */
+ guint32 mul; /* multiple :ML_TABLE[ML] */
+ guint32 Cnt; /* frequency count */
+ guint32 Incr; /* frequency step */
+
+ /* envelope generator state */
+ guint8 eg_typ;/* envelope type flag */
+ guint8 evm; /* envelope phase */
+ int evc; /* envelope counter */
+ int eve; /* envelope counter end point */
+ int evs; /* envelope counter step */
+ int evsa; /* envelope step for AR :AR[ksr] */
+ int evsd; /* envelope step for DR :DR[ksr] */
+ int evsr; /* envelope step for RR :RR[ksr] */
+
+ /* LFO */
+ guint8 ams; /* ams flag */
+ guint8 vib; /* vibrate flag */
+ /* wave selector */
+ int **wavetable;
+} OPL_SLOT;
+
+/* ---------- OPL one of channel ---------- */
+typedef struct fm_opl_channel {
+ OPL_SLOT SLOT[2];
+ guint8 CON; /* connection type */
+ guint8 FB; /* feed back :(shift down bit)*/
+ int *connect1; /* slot1 output pointer */
+ int *connect2; /* slot2 output pointer */
+ int op1_out[2]; /* slot1 output for selfeedback */
+
+ /* phase generator state */
+ guint32 block_fnum; /* block+fnum */
+ guint8 kcode; /* key code : KeyScaleCode */
+ guint32 fc; /* Freq. Increment base */
+ guint32 ksl_base; /* KeyScaleLevel Base step */
+ guint8 keyon; /* key on/off flag */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+ guint8 type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time) */
+ guint8 address; /* address register */
+ guint8 status; /* status flag */
+ guint8 statusmask; /* status mask */
+ guint32 mode; /* Reg.08 : CSM , notesel,etc. */
+
+ /* Timer */
+ int T[2]; /* timer counter */
+ guint8 st[2]; /* timer enable */
+
+ /* FM channel slots */
+ OPL_CH *P_CH; /* pointer of CH */
+ int max_ch; /* maximum channel */
+
+ /* Rythm sention */
+ guint8 rythm; /* Rythm mode , key flag */
+
+ /* time tables */
+ int AR_TABLE[75]; /* atttack rate tables */
+ int DR_TABLE[75]; /* decay rate tables */
+ guint32 FN_TABLE[1024];/* fnumber -> increment counter */
+
+ /* LFO */
+ int *ams_table;
+ int *vib_table;
+ int amsCnt;
+ int amsIncr;
+ int vibCnt;
+ int vibIncr;
+
+ /* wave selector enable flag */
+ guint8 wavesel;
+
+ /* external event callback handler */
+ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
+ int TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ int IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ int UpdateParam; /* stream update parameter */
+} FM_OPL;
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+
+void OPLBuildTables(int ENV_BITS_PARAM, int EG_ENT_PARAM);
+
+FM_OPL *OPLCreate(int type, int clock, int rate);
+void OPLDestroy(FM_OPL *OPL);
+void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler, int channelOffset);
+void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param);
+void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler, int param);
+
+void OPLResetChip(FM_OPL *OPL);
+int OPLWrite(FM_OPL *OPL, int a, int v);
+unsigned char OPLRead(FM_OPL *OPL, int a);
+int OPLTimerOver(FM_OPL *OPL, int c);
+void OPLWriteReg(FM_OPL *OPL, int r, int v);
+void YM3812UpdateOne(FM_OPL *OPL, gint16 *buffer, int length, int interleave);
+#endif
diff --git a/engines/sci/sfx/softseq/mt32.cpp b/engines/sci/sfx/softseq/mt32.cpp
new file mode 100644
index 0000000000..6b37673c9e
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32.cpp
@@ -0,0 +1,228 @@
+/***************************************************************************
+ Copyright (C) 2008 Walter van Niftrik
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantability,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+extern "C" {
+#include "../softseq.h"
+#include "../sequencer.h"
+#include "../device.h"
+#include "resource.h"
+#include "sci_memory.h"
+}
+
+#include "mt32/mt32emu.h"
+
+/* FIXME */
+#define FREESCI_GAMEDIR ".freesci"
+
+#define SAMPLE_RATE 32000
+#define CHANNELS SFX_PCM_STEREO_LR
+#define STEREO 1
+
+static MT32Emu::Synth *synth;
+static sfx_sequencer_t *mt32seq;
+static int initializing;
+
+/* MIDI writer */
+
+static int
+mt32_midi_init(struct _midi_writer *self)
+{
+ return SFX_OK;
+}
+
+static int
+mt32_midi_set_option(struct _midi_writer *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+mt32_midi_write(struct _midi_writer *self, unsigned char *buf, int len)
+{
+ if (buf[0] == 0xf0)
+ synth->playSysex(buf, len);
+ else if (len < 4) {
+ MT32Emu::Bit32u msg;
+ msg = buf[0];
+ if (len > 0)
+ msg |= buf[1] << 8;
+ if (len > 1)
+ msg |= buf[2] << 16;
+ synth->playMsg(msg);
+ }
+ else
+ sciprintf("MT32EMU: Skipping non-sysex message of more than 3 bytes.\n");
+
+ return SFX_OK;
+}
+
+static void
+mt32_midi_delay(struct _midi_writer *self, int ticks)
+{
+}
+
+static void
+mt32_midi_reset_timer(struct _midi_writer *self)
+{
+}
+
+static void
+mt32_midi_close(struct _midi_writer *self)
+{
+}
+
+static midi_writer_t midi_writer_mt32 = {
+ "mt32",
+ mt32_midi_init,
+ mt32_midi_set_option,
+ mt32_midi_write,
+ mt32_midi_delay,
+ NULL,
+ mt32_midi_reset_timer,
+ mt32_midi_close
+};
+
+/* Software sequencer */
+
+static void printDebug(void *userData, const char *fmt, va_list list)
+{
+ if (initializing) {
+ vprintf(fmt, list);
+ printf("\n");
+ }
+}
+
+static void
+mt32_poll(sfx_softseq_t *self, byte *dest, int count)
+{
+ synth->render((MT32Emu::Bit16s *) dest, count);
+}
+
+static int
+mt32_init(sfx_softseq_t *self, byte *data_ptr, int data_length, byte *data2_ptr,
+ int data2_length)
+{
+ MT32Emu::SynthProperties prop;
+ char *home = sci_get_homedir();
+ char *romdir;
+
+ if (!home) {
+ sciprintf("MT32EMU: Couldn't determine home directory.\n");
+ return SFX_ERROR;
+ }
+
+ romdir = (char *) sci_malloc(sizeof(home) + 2 * sizeof(G_DIR_SEPARATOR_S)
+ + sizeof(FREESCI_GAMEDIR) + 1);
+
+ strcpy(romdir, home);
+ strcat(romdir, G_DIR_SEPARATOR_S);
+ strcat(romdir, FREESCI_GAMEDIR);
+ strcat(romdir, G_DIR_SEPARATOR_S);
+
+ mt32seq = sfx_find_sequencer("MT32");
+ if (!mt32seq) {
+ sciprintf("MT32EMU: Unable to find MT32 sequencer.\n");
+ return SFX_ERROR;
+ }
+
+ prop.sampleRate = SAMPLE_RATE;
+ prop.useReverb = true;
+ prop.useDefaultReverb = true;
+ prop.baseDir = romdir;
+ prop.userData = NULL;
+ prop.report = NULL;
+ prop.printDebug = printDebug;
+ prop.openFile = NULL;
+ prop.closeFile = NULL;
+
+ initializing = 1;
+ synth = new MT32Emu::Synth();
+ if (!synth->open(prop))
+ return SFX_ERROR;
+ initializing = 0;
+
+ mt32seq->open(data_length, data_ptr, data2_length, data2_ptr,
+ &midi_writer_mt32);
+
+ free(romdir);
+
+ return SFX_OK;
+}
+
+static void
+mt32_exit(sfx_softseq_t *self)
+{
+ synth->close();
+ delete synth;
+
+ mt32seq->close();
+}
+
+static void
+mt32_allstop(sfx_softseq_t *self)
+{
+ if (mt32seq->allstop)
+ mt32seq->allstop();
+}
+
+static void
+mt32_volume(sfx_softseq_t *self, int volume)
+{
+ if (mt32seq->volume)
+ mt32seq->volume(volume / 2); /* High volume causes clipping. */
+}
+
+static int
+mt32_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static void
+mt32_event(sfx_softseq_t *self, byte cmd, int argc, byte *argv)
+{
+ mt32seq->event(cmd, argc, argv);
+}
+
+sfx_softseq_t sfx_softseq_mt32 = {
+ "mt32emu",
+ "0.1",
+ mt32_set_option,
+ mt32_init,
+ mt32_exit,
+ mt32_volume,
+ mt32_event,
+ mt32_poll,
+ mt32_allstop,
+ NULL,
+ 001, /* patch.001 */
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x01, /* playflag */
+ 1, /* do play channel 9 */
+ 32, /* Max polypgony */
+ {SAMPLE_RATE, CHANNELS, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/mt32/Makefile.am b/engines/sci/sfx/softseq/mt32/Makefile.am
new file mode 100644
index 0000000000..5d2ffa31c8
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/Makefile.am
@@ -0,0 +1,5 @@
+noinst_HEADERS = freeverb.h mt32emu.h part.h partialManager.h synth.h \
+i386.h mt32_file.h partial.h structures.h tables.h
+noinst_LIBRARIES = libmt32emu.a
+libmt32emu_a_SOURCES = freeverb.cpp mt32_file.cpp partial.cpp synth.cpp \
+i386.cpp part.cpp partialManager.cpp tables.cpp
diff --git a/engines/sci/sfx/softseq/mt32/freeverb.cpp b/engines/sci/sfx/softseq/mt32/freeverb.cpp
new file mode 100644
index 0000000000..1c3aab0494
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/freeverb.cpp
@@ -0,0 +1,310 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Comb filter implementation
+//
+// Written by
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#include "freeverb.h"
+
+comb::comb() {
+ filterstore = 0;
+ bufidx = 0;
+}
+
+void comb::setbuffer(float *buf, int size) {
+ buffer = buf;
+ bufsize = size;
+}
+
+void comb::mute() {
+ for (int i = 0; i < bufsize; i++)
+ buffer[i] = 0;
+}
+
+void comb::setdamp(float val) {
+ damp1 = val;
+ damp2 = 1 - val;
+}
+
+float comb::getdamp() {
+ return damp1;
+}
+
+void comb::setfeedback(float val) {
+ feedback = val;
+}
+
+float comb::getfeedback() {
+ return feedback;
+}
+
+// Allpass filter implementation
+
+allpass::allpass() {
+ bufidx = 0;
+}
+
+void allpass::setbuffer(float *buf, int size) {
+ buffer = buf;
+ bufsize = size;
+}
+
+void allpass::mute() {
+ for (int i = 0; i < bufsize; i++)
+ buffer[i] = 0;
+}
+
+void allpass::setfeedback(float val) {
+ feedback = val;
+}
+
+float allpass::getfeedback() {
+ return feedback;
+}
+
+// Reverb model implementation
+
+revmodel::revmodel() {
+ // Tie the components to their buffers
+ combL[0].setbuffer(bufcombL1,combtuningL1);
+ combR[0].setbuffer(bufcombR1,combtuningR1);
+ combL[1].setbuffer(bufcombL2,combtuningL2);
+ combR[1].setbuffer(bufcombR2,combtuningR2);
+ combL[2].setbuffer(bufcombL3,combtuningL3);
+ combR[2].setbuffer(bufcombR3,combtuningR3);
+ combL[3].setbuffer(bufcombL4,combtuningL4);
+ combR[3].setbuffer(bufcombR4,combtuningR4);
+ combL[4].setbuffer(bufcombL5,combtuningL5);
+ combR[4].setbuffer(bufcombR5,combtuningR5);
+ combL[5].setbuffer(bufcombL6,combtuningL6);
+ combR[5].setbuffer(bufcombR6,combtuningR6);
+ combL[6].setbuffer(bufcombL7,combtuningL7);
+ combR[6].setbuffer(bufcombR7,combtuningR7);
+ combL[7].setbuffer(bufcombL8,combtuningL8);
+ combR[7].setbuffer(bufcombR8,combtuningR8);
+ allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
+ allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
+ allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
+ allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
+ allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
+ allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
+ allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
+ allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
+
+ // Set default values
+ allpassL[0].setfeedback(0.5f);
+ allpassR[0].setfeedback(0.5f);
+ allpassL[1].setfeedback(0.5f);
+ allpassR[1].setfeedback(0.5f);
+ allpassL[2].setfeedback(0.5f);
+ allpassR[2].setfeedback(0.5f);
+ allpassL[3].setfeedback(0.5f);
+ allpassR[3].setfeedback(0.5f);
+ setwet(initialwet);
+ setroomsize(initialroom);
+ setdry(initialdry);
+ setdamp(initialdamp);
+ setwidth(initialwidth);
+ setmode(initialmode);
+
+ // Buffer will be full of rubbish - so we MUST mute them
+ mute();
+}
+
+void revmodel::mute() {
+ int i;
+
+ if (getmode() >= freezemode)
+ return;
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].mute();
+ combR[i].mute();
+ }
+
+ for (i = 0; i < numallpasses; i++) {
+ allpassL[i].mute();
+ allpassR[i].mute();
+ }
+}
+
+void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
+ float outL, outR, input;
+
+ while (numsamples-- > 0) {
+ int i;
+
+ outL = outR = 0;
+ input = (*inputL + *inputR) * gain;
+
+ // Accumulate comb filters in parallel
+ for (i = 0; i < numcombs; i++) {
+ outL += combL[i].process(input);
+ outR += combR[i].process(input);
+ }
+
+ // Feed through allpasses in series
+ for (i = 0; i < numallpasses; i++) {
+ outL = allpassL[i].process(outL);
+ outR = allpassR[i].process(outR);
+ }
+
+ // Calculate output REPLACING anything already there
+ *outputL = outL * wet1 + outR * wet2 + *inputL * dry;
+ *outputR = outR * wet1 + outL * wet2 + *inputR * dry;
+
+ // Increment sample pointers, allowing for interleave (if any)
+ inputL += skip;
+ inputR += skip;
+ outputL += skip;
+ outputR += skip;
+ }
+}
+
+void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
+ float outL, outR, input;
+
+ while (numsamples-- > 0) {
+ int i;
+
+ outL = outR = 0;
+ input = (*inputL + *inputR) * gain;
+
+ // Accumulate comb filters in parallel
+ for (i = 0; i < numcombs; i++) {
+ outL += combL[i].process(input);
+ outR += combR[i].process(input);
+ }
+
+ // Feed through allpasses in series
+ for (i = 0; i < numallpasses; i++) {
+ outL = allpassL[i].process(outL);
+ outR = allpassR[i].process(outR);
+ }
+
+ // Calculate output MIXING with anything already there
+ *outputL += outL * wet1 + outR * wet2 + *inputL * dry;
+ *outputR += outR * wet1 + outL * wet2 + *inputR * dry;
+
+ // Increment sample pointers, allowing for interleave (if any)
+ inputL += skip;
+ inputR += skip;
+ outputL += skip;
+ outputR += skip;
+ }
+}
+
+void revmodel::update() {
+ // Recalculate internal values after parameter change
+
+ int i;
+
+ wet1 = wet * (width / 2 + 0.5f);
+ wet2 = wet * ((1 - width) / 2);
+
+ if (mode >= freezemode) {
+ roomsize1 = 1;
+ damp1 = 0;
+ gain = muted;
+ } else {
+ roomsize1 = roomsize;
+ damp1 = damp;
+ gain = fixedgain;
+ }
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].setfeedback(roomsize1);
+ combR[i].setfeedback(roomsize1);
+ }
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].setdamp(damp1);
+ combR[i].setdamp(damp1);
+ }
+}
+
+// The following get/set functions are not inlined, because
+// speed is never an issue when calling them, and also
+// because as you develop the reverb model, you may
+// wish to take dynamic action when they are called.
+
+void revmodel::setroomsize(float value) {
+ roomsize = (value * scaleroom) + offsetroom;
+ update();
+}
+
+float revmodel::getroomsize() {
+ return (roomsize - offsetroom) / scaleroom;
+}
+
+void revmodel::setdamp(float value) {
+ damp = value * scaledamp;
+ update();
+}
+
+float revmodel::getdamp() {
+ return damp / scaledamp;
+}
+
+void revmodel::setwet(float value) {
+ wet = value * scalewet;
+ update();
+}
+
+float revmodel::getwet() {
+ return wet / scalewet;
+}
+
+void revmodel::setdry(float value) {
+ dry = value * scaledry;
+}
+
+float revmodel::getdry() {
+ return dry / scaledry;
+}
+
+void revmodel::setwidth(float value) {
+ width = value;
+ update();
+}
+
+float revmodel::getwidth() {
+ return width;
+}
+
+void revmodel::setmode(float value) {
+ mode = value;
+ update();
+}
+
+float revmodel::getmode() {
+ if (mode >= freezemode)
+ return 1;
+ else
+ return 0;
+}
diff --git a/engines/sci/sfx/softseq/mt32/freeverb.h b/engines/sci/sfx/softseq/mt32/freeverb.h
new file mode 100644
index 0000000000..53c5307c5a
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/freeverb.h
@@ -0,0 +1,239 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Macro for killing denormalled numbers
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// Based on IS_DENORMAL macro by Jon Watte
+// This code is public domain
+
+#ifndef FREEVERB_H
+#define FREEVERB_H
+
+#define undenormalise(sample) if (((*(unsigned int*)&sample) & 0x7f800000) == 0) sample = 0.0f
+
+// Comb filter class declaration
+
+class comb {
+public:
+ comb();
+ void setbuffer(float *buf, int size);
+ inline float process(float inp);
+ void mute();
+ void setdamp(float val);
+ float getdamp();
+ void setfeedback(float val);
+ float getfeedback();
+private:
+ float feedback;
+ float filterstore;
+ float damp1;
+ float damp2;
+ float *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float comb::process(float input) {
+ float output;
+
+ output = buffer[bufidx];
+ undenormalise(output);
+
+ filterstore = (output * damp2) + (filterstore * damp1);
+ undenormalise(filterstore);
+
+ buffer[bufidx] = input + (filterstore * feedback);
+
+ if (++bufidx >= bufsize)
+ bufidx = 0;
+
+ return output;
+}
+
+// Allpass filter declaration
+
+class allpass {
+public:
+ allpass();
+ void setbuffer(float *buf, int size);
+ inline float process(float inp);
+ void mute();
+ void setfeedback(float val);
+ float getfeedback();
+private:
+ float feedback;
+ float *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float allpass::process(float input) {
+ float output;
+ float bufout;
+
+ bufout = buffer[bufidx];
+ undenormalise(bufout);
+
+ output = -input + bufout;
+ buffer[bufidx] = input + (bufout * feedback);
+
+ if (++bufidx >= bufsize)
+ bufidx = 0;
+
+ return output;
+}
+
+
+// Reverb model tuning values
+
+const int numcombs = 8;
+const int numallpasses = 4;
+const float muted = 0;
+const float fixedgain = 0.015f;
+const float scalewet = 3;
+const float scaledry = 2;
+const float scaledamp = 0.4f;
+const float scaleroom = 0.28f;
+const float offsetroom = 0.7f;
+const float initialroom = 0.5f;
+const float initialdamp = 0.5f;
+const float initialwet = 1 / scalewet;
+const float initialdry = 0;
+const float initialwidth = 1;
+const float initialmode = 0;
+const float freezemode = 0.5f;
+const int stereospread = 23;
+
+// These values assume 44.1KHz sample rate
+// they will probably be OK for 48KHz sample rate
+// but would need scaling for 96KHz (or other) sample rates.
+// The values were obtained by listening tests.
+const int combtuningL1 = 1116;
+const int combtuningR1 = 1116 + stereospread;
+const int combtuningL2 = 1188;
+const int combtuningR2 = 1188 + stereospread;
+const int combtuningL3 = 1277;
+const int combtuningR3 = 1277 + stereospread;
+const int combtuningL4 = 1356;
+const int combtuningR4 = 1356 + stereospread;
+const int combtuningL5 = 1422;
+const int combtuningR5 = 1422 + stereospread;
+const int combtuningL6 = 1491;
+const int combtuningR6 = 1491 + stereospread;
+const int combtuningL7 = 1557;
+const int combtuningR7 = 1557 + stereospread;
+const int combtuningL8 = 1617;
+const int combtuningR8 = 1617 + stereospread;
+const int allpasstuningL1 = 556;
+const int allpasstuningR1 = 556 + stereospread;
+const int allpasstuningL2 = 441;
+const int allpasstuningR2 = 441 + stereospread;
+const int allpasstuningL3 = 341;
+const int allpasstuningR3 = 341 + stereospread;
+const int allpasstuningL4 = 225;
+const int allpasstuningR4 = 225 + stereospread;
+
+
+// Reverb model declaration
+
+class revmodel {
+public:
+ revmodel();
+ void mute();
+ void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
+ void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
+ void setroomsize(float value);
+ float getroomsize();
+ void setdamp(float value);
+ float getdamp();
+ void setwet(float value);
+ float getwet();
+ void setdry(float value);
+ float getdry();
+ void setwidth(float value);
+ float getwidth();
+ void setmode(float value);
+ float getmode();
+private:
+ void update();
+
+ float gain;
+ float roomsize, roomsize1;
+ float damp, damp1;
+ float wet, wet1, wet2;
+ float dry;
+ float width;
+ float mode;
+
+ // The following are all declared inline
+ // to remove the need for dynamic allocation
+ // with its subsequent error-checking messiness
+
+ // Comb filters
+ comb combL[numcombs];
+ comb combR[numcombs];
+
+ // Allpass filters
+ allpass allpassL[numallpasses];
+ allpass allpassR[numallpasses];
+
+ // Buffers for the combs
+ float bufcombL1[combtuningL1];
+ float bufcombR1[combtuningR1];
+ float bufcombL2[combtuningL2];
+ float bufcombR2[combtuningR2];
+ float bufcombL3[combtuningL3];
+ float bufcombR3[combtuningR3];
+ float bufcombL4[combtuningL4];
+ float bufcombR4[combtuningR4];
+ float bufcombL5[combtuningL5];
+ float bufcombR5[combtuningR5];
+ float bufcombL6[combtuningL6];
+ float bufcombR6[combtuningR6];
+ float bufcombL7[combtuningL7];
+ float bufcombR7[combtuningR7];
+ float bufcombL8[combtuningL8];
+ float bufcombR8[combtuningR8];
+
+ // Buffers for the allpasses
+ float bufallpassL1[allpasstuningL1];
+ float bufallpassR1[allpasstuningR1];
+ float bufallpassL2[allpasstuningL2];
+ float bufallpassR2[allpasstuningR2];
+ float bufallpassL3[allpasstuningL3];
+ float bufallpassR3[allpasstuningR3];
+ float bufallpassL4[allpasstuningL4];
+ float bufallpassR4[allpasstuningR4];
+};
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/i386.cpp b/engines/sci/sfx/softseq/mt32/i386.cpp
new file mode 100644
index 0000000000..f092189d76
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/i386.cpp
@@ -0,0 +1,849 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "mt32emu.h"
+
+#ifdef MT32EMU_HAVE_X86
+
+namespace MT32Emu {
+
+#ifndef _MSC_VER
+
+#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value))
+#define cpuid_flag (1 << 21)
+
+static inline bool atti386_DetectCPUID() {
+ unsigned int result;
+
+ // Is there a cpuid?
+ result = cpuid_flag; // set test
+ eflag(result);
+ if (!(result & cpuid_flag))
+ return false;
+
+ result = 0; // clear test
+ eflag(result);
+ if (result & cpuid_flag)
+ return false;
+
+ return true;
+}
+
+static inline bool atti386_DetectSIMD() {
+ unsigned int result;
+
+ if (atti386_DetectCPUID() == false)
+ return false;
+
+ /* check cpuid */
+ __asm__ __volatile__(
+ "pushl %%ebx \n" \
+ "movl $1, %%eax \n" \
+ "cpuid \n" \
+ "movl %%edx, %0 \n" \
+ "popl %%ebx \n" \
+ : "=r"(result) : : "eax", "ecx", "edx");
+
+ if (result & (1 << 25))
+ return true;
+
+ return false;
+}
+
+static inline bool atti386_Detect3DNow() {
+ unsigned int result;
+
+ if (atti386_DetectCPUID() == false)
+ return false;
+
+ // get cpuid
+ __asm__ __volatile__(
+ "pushl %%ebx \n" \
+ "movl $0x80000001, %%eax \n" \
+ "cpuid \n" \
+ "movl %%edx, %0 \n" \
+ "popl %%ebx \n" \
+ : "=r"(result) : : "eax", "ecx", "edx");
+
+ if (result & 0x80000000)
+ return true;
+
+ return false;
+}
+
+
+static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr) {
+ __asm__ __volatile__ (
+ "pushl %1 \n" \
+ "pushl %2 \n" \
+ "movss 0(%0), %%xmm1 \n" \
+ "movups 0(%1), %%xmm2 \n" \
+ "movlps 0(%2), %%xmm3 \n" \
+ " \n" \
+ "shufps $0x44, %%xmm3, %%xmm3 \n" \
+ " \n" \
+ "mulps %%xmm3, %%xmm2 \n" \
+ " \n" \
+ "subss %%xmm2, %%xmm1 \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "subss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "movss %%xmm1, 0(%2) \n" \
+ " \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "addss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "addss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "movss %%xmm3, 4(%2) \n" \
+ " \n" \
+ "addl $16, %1 \n" \
+ "addl $8, %2 \n" \
+ " \n" \
+ "movups 0(%1), %%xmm2 \n" \
+ " \n" \
+ "movlps 0(%2), %%xmm3 \n" \
+ "shufps $0x44, %%xmm3, %%xmm3 \n" \
+ " \n" \
+ "mulps %%xmm3, %%xmm2 \n" \
+ " \n" \
+ "subss %%xmm2, %%xmm1 \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "subss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "movss %%xmm1, 0(%2) \n" \
+ " \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "addss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "addss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "movss %%xmm3, 4(%2) \n" \
+ "movss %%xmm1, 0(%0) \n" \
+ "popl %2 \n" \
+ "popl %1 \n" \
+ : : "r"(output), "r"(coef_ptr), "r"(hist1_ptr)
+ : "memory"
+#ifdef __SSE__
+ , "xmm1", "xmm2", "xmm3"
+#endif
+ );
+
+ return *output;
+}
+
+static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr) {
+ float tmp;
+
+ __asm__ __volatile__ (
+ "movq %0, %%mm1 \n" \
+ " \n" \
+ "movl %1, %%edi \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ " \n" \
+ "movl %2, %%eax; \n" \
+ "movq 0(%%eax), %%mm3 \n" \
+ " \n" \
+ "pfmul %%mm3, %%mm2 \n" \
+ "pfsub %%mm2, %%mm1 \n" \
+ " \n" \
+ "psrlq $32, %%mm2 \n" \
+ "pfsub %%mm2, %%mm1 \n" \
+ " \n" \
+ "movd %%mm1, %3 \n" \
+ " \n" \
+ "addl $8, %%edi \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "movq 0(%%eax), %%mm3 \n" \
+ " \n" \
+ "pfmul %%mm3, %%mm2 \n" \
+ "pfadd %%mm2, %%mm1 \n" \
+ " \n" \
+ "psrlq $32, %%mm2 \n" \
+ "pfadd %%mm2, %%mm1 \n" \
+ " \n" \
+ "pushl %3 \n" \
+ "popl 0(%%eax) \n" \
+ " \n" \
+ "movd %%mm3, 4(%%eax) \n" \
+ " \n" \
+ "addl $8, %%edi \n" \
+ "addl $8, %%eax \n" \
+ " \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "movq 0(%%eax), %%mm3 \n" \
+ " \n" \
+ "pfmul %%mm3, %%mm2 \n" \
+ "pfsub %%mm2, %%mm1 \n" \
+ " \n" \
+ "psrlq $32, %%mm2 \n" \
+ "pfsub %%mm2, %%mm1 \n" \
+ " \n" \
+ "movd %%mm1, %3 \n" \
+ " \n" \
+ "addl $8, %%edi \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "movq 0(%%eax), %%mm3 \n" \
+ " \n" \
+ "pfmul %%mm3, %%mm2 \n" \
+ "pfadd %%mm2, %%mm1 \n" \
+ " \n" \
+ "psrlq $32, %%mm2 \n" \
+ "pfadd %%mm2, %%mm1 \n" \
+ " \n" \
+ "pushl %3 \n" \
+ "popl 0(%%eax) \n" \
+ "movd %%mm3, 4(%%eax) \n" \
+ " \n" \
+ "movd %%mm1, %0 \n" \
+ "femms \n" \
+ : "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp)
+ : "eax", "edi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2", "mm3"
+#endif
+ );
+
+ return output;
+}
+
+static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movw %1, %%ax \n" \
+ "shll $16, %%eax \n" \
+ "movw %1, %%ax \n" \
+ "movd %%eax, %%mm3 \n" \
+ "movd %%eax, %%mm2 \n" \
+ "psllq $32, %%mm3 \n" \
+ "por %%mm2, %%mm3 \n" \
+ "movl %2, %%esi \n" \
+ "movl %3, %%edi \n" \
+ "1: \n" \
+ "movq 0(%%esi), %%mm1 \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "pmulhw %%mm3, %%mm1 \n" \
+ "paddw %%mm2, %%mm1 \n" \
+ "movq %%mm1, 0(%%edi) \n" \
+ " \n" \
+ "addl $8, %%esi \n" \
+ "addl $8, %%edi \n" \
+ " \n" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ : : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd)
+ : "eax", "ecx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2", "mm3"
+#endif
+ );
+}
+
+static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor) {
+ __asm__ __volatile__(
+ "movl %4, %%ecx \n" \
+ "shrl $1, %%ecx \n" \
+ "addl $4, %%ecx \n" \
+ "pushl %%ecx \n" \
+ " \n" \
+ "movl %0, %%esi \n" \
+ "movups 0(%%esi), %%xmm1 \n" \
+ " \n" \
+ "movl %1, %%esi \n" \
+ "movl %2, %%edi \n" \
+ "1: \n" \
+ "xorl %%eax, %%eax \n" \
+ "movw 0(%1), %%ax \n" \
+ "cwde \n" \
+ "incl %1 \n" \
+ "incl %1 \n" \
+ "movd %%eax, %%mm1 \n" \
+ "psrlq $32, %%mm1 \n" \
+ "movw 0(%1), %%ax \n" \
+ "incl %1 \n" \
+ "incl %1 \n" \
+ "movd %%eax, %%mm2 \n" \
+ "por %%mm2, %%mm1 \n" \
+ " \n" \
+ "decl %%ecx \n" \
+ "jnz 1b \n" \
+ " \n" \
+ "popl %%ecx \n" \
+ "movl %1, %%esi \n" \
+ "movl %3, %%edi \n" \
+ "incl %%esi \n" \
+ "2: \n" \
+ "decl %%ecx \n" \
+ "jnz 2b \n" \
+ : : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len)
+ : "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory");
+}
+
+static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movl %1, %%esi \n" \
+ "movl %2, %%edi \n" \
+ "1: \n" \
+ "movq 0(%%edi), %%mm1 \n" \
+ "movq 0(%%esi), %%mm2 \n" \
+ "paddw %%mm2, %%mm1 \n" \
+ "movq %%mm1, 0(%%esi) \n" \
+ "addl $8, %%edi \n" \
+ "addl $8, %%esi \n" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ : : "g"(len), "g"(buf1), "g"(buf2)
+ : "ecx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2"
+#endif
+ );
+}
+
+static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movl %1, %%esi \n" \
+ "movl %2, %%edi \n" \
+ "1: \n" \
+ "movq 0(%%esi), %%mm1 \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "movq %%mm1, %%mm3 \n" \
+ "pmulhw %%mm2, %%mm1 \n" \
+ "paddw %%mm3, %%mm1 \n" \
+ "movq %%mm1, 0(%%esi) \n" \
+ "addl $8, %%edi \n" \
+ "addl $8, %%esi \n" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ : : "g"(len), "g"(buf1), "g"(buf2)
+ : "ecx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2", "mm3"
+#endif
+ );
+}
+
+static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movl %1, %%esi \n" \
+ "movl %2, %%edi \n" \
+ "1: \n" \
+ "movq 0(%%esi), %%mm1 \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "pmulhw %%mm2, %%mm1 \n" \
+ "movq %%mm1, 0(%%esi) \n" \
+ "addl $8, %%edi \n" \
+ "addl $8, %%esi \n" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ : : "g"(len), "g"(buf1), "g"(buf2)
+ : "ecx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2"
+#endif
+ );
+}
+
+static inline void atti386_partialProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *p1buf) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movw %1, %%ax \n" \
+ "shll $16, %%eax \n" \
+ "movw %2, %%ax \n" \
+ "movd %%eax, %%mm1 \n" \
+ "movd %%eax, %%mm2 \n" \
+ "psllq $32, %%mm1 \n" \
+ "por %%mm2, %%mm1 \n" \
+ "movl %3, %%edi \n" \
+ "movl %4, %%esi \n" \
+ "pushl %%ebx \n" \
+ "1: \n" \
+ "movw 0(%%esi), %%bx \n" \
+ "addl $2, %%esi \n" \
+ "movw 0(%%esi), %%dx \n" \
+ "addl $2, %%esi \n" \
+ "" \
+ "movw %%dx, %%ax \n" \
+ "shll $16, %%eax \n" \
+ "movw %%dx, %%ax \n" \
+ "movd %%eax, %%mm2 \n" \
+ "psllq $32, %%mm2 \n" \
+ "movw %%bx, %%ax \n" \
+ "shll $16, %%eax \n" \
+ "movw %%bx, %%ax \n" \
+ "movd %%eax, %%mm3 \n" \
+ "por %%mm3, %%mm2 \n" \
+ "" \
+ "pmulhw %%mm1, %%mm2 \n" \
+ "movq %%mm2, 0(%%edi) \n" \
+ "addl $8, %%edi \n" \
+ "" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ "popl %%ebx \n" \
+ : : "g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf)
+ : "eax", "ecx", "edx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2", "mm3"
+#endif
+ );
+}
+
+#endif
+
+bool DetectSIMD() {
+#ifdef _MSC_VER
+ bool found_simd;
+ __asm {
+ pushfd
+ pop eax // get EFLAGS into eax
+ mov ebx,eax // keep a copy
+ xor eax,0x200000
+ // toggle CPUID bit
+
+ push eax
+ popfd // set new EFLAGS
+ pushfd
+ pop eax // EFLAGS back into eax
+
+ xor eax,ebx
+ // have we changed the ID bit?
+
+ je NO_SIMD
+ // No, no CPUID instruction
+
+ // we could toggle the
+ // ID bit so CPUID is present
+ mov eax,1
+
+ cpuid // get processor features
+ test edx,1<<25 // check the SIMD bit
+ jz NO_SIMD
+ mov found_simd,1
+ jmp DONE
+ NO_SIMD:
+ mov found_simd,0
+ DONE:
+ }
+ return found_simd;
+#else
+ return atti386_DetectSIMD();
+#endif
+}
+
+bool Detect3DNow() {
+#ifdef _MSC_VER
+ bool found3D = false;
+ __asm {
+ pushfd
+ pop eax
+ mov edx, eax
+ xor eax, 00200000h
+ push eax
+ popfd
+ pushfd
+ pop eax
+ xor eax, edx
+ jz NO_3DNOW
+
+ mov eax, 80000000h
+ cpuid
+
+ cmp eax, 80000000h
+ jbe NO_3DNOW
+
+ mov eax, 80000001h
+ cpuid
+ test edx, 80000000h
+ jz NO_3DNOW
+ mov found3D, 1
+NO_3DNOW:
+
+ }
+ return found3D;
+#else
+ return atti386_Detect3DNow();
+#endif
+}
+
+float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr) {
+ float output;
+
+ // 1st number of coefficients array is overall input scale factor, or filter gain
+ output = input * (*coef_ptr++);
+
+#ifdef _MSC_VER
+ __asm {
+
+ movss xmm1, output
+
+ mov eax, coef_ptr
+ movups xmm2, [eax]
+
+ mov eax, hist1_ptr
+ movlps xmm3, [eax]
+ shufps xmm3, xmm3, 44h
+ // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr
+
+ mulps xmm2, xmm3
+
+ subss xmm1, xmm2
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ subss xmm1, xmm2
+
+ // Store new_hist
+ movss DWORD PTR [eax], xmm1
+
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ addss xmm1, xmm2
+
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ addss xmm1, xmm2
+
+ // Store previous hist
+ movss DWORD PTR [eax+4], xmm3
+
+ add coef_ptr, 16
+ add hist1_ptr, 8
+
+ mov eax, coef_ptr
+ movups xmm2, [eax]
+
+ mov eax, hist1_ptr
+ movlps xmm3, [eax]
+ shufps xmm3, xmm3, 44h
+ // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr
+
+ mulps xmm2, xmm3
+
+ subss xmm1, xmm2
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ subss xmm1, xmm2
+
+ // Store new_hist
+ movss DWORD PTR [eax], xmm1
+
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ addss xmm1, xmm2
+
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ addss xmm1, xmm2
+
+ // Store previous hist
+ movss DWORD PTR [eax+4], xmm3
+
+ movss output, xmm1
+ }
+#else
+ output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr);
+#endif
+ return output;
+}
+
+float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr) {
+ float output;
+
+ // 1st number of coefficients array is overall input scale factor, or filter gain
+ output = input * (*coef_ptr++);
+
+ // I find it very sad that 3DNow requires twice as many instructions as Intel's SSE
+ // Intel does have the upper hand here.
+#ifdef _MSC_VER
+ float tmp;
+ __asm {
+ movq mm1, output
+ mov ebx, coef_ptr
+ movq mm2, [ebx]
+
+ mov eax, hist1_ptr;
+ movq mm3, [eax]
+
+ pfmul mm2, mm3
+ pfsub mm1, mm2
+
+ psrlq mm2, 32
+ pfsub mm1, mm2
+
+ // Store new hist
+ movd tmp, mm1
+
+ add ebx, 8
+ movq mm2, [ebx]
+ movq mm3, [eax]
+
+ pfmul mm2, mm3
+ pfadd mm1, mm2
+
+ psrlq mm2, 32
+ pfadd mm1, mm2
+
+ push tmp
+ pop DWORD PTR [eax]
+
+ movd DWORD PTR [eax+4], mm3
+
+ add ebx, 8
+ add eax, 8
+
+ movq mm2, [ebx]
+ movq mm3, [eax]
+
+ pfmul mm2, mm3
+ pfsub mm1, mm2
+
+ psrlq mm2, 32
+ pfsub mm1, mm2
+
+ // Store new hist
+ movd tmp, mm1
+
+ add ebx, 8
+ movq mm2, [ebx]
+ movq mm3, [eax]
+
+ pfmul mm2, mm3
+ pfadd mm1, mm2
+
+ psrlq mm2, 32
+ pfadd mm1, mm2
+
+ push tmp
+ pop DWORD PTR [eax]
+ movd DWORD PTR [eax+4], mm3
+
+ movd output, mm1
+
+ femms
+ }
+#else
+ output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr);
+#endif
+ return output;
+}
+
+#if MT32EMU_USE_MMX > 0
+
+int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf) {
+ int tmplen = len >> 1;
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx,tmplen
+ mov ax, leftvol
+ shl eax,16
+ mov ax, rightvol
+ movd mm1, eax
+ movd mm2, eax
+ psllq mm1, 32
+ por mm1, mm2
+ mov edi, partialBuf
+ mov esi, mixedBuf
+mmxloop1:
+ mov bx, [esi]
+ add esi,2
+ mov dx, [esi]
+ add esi,2
+
+ mov ax, dx
+ shl eax, 16
+ mov ax, dx
+ movd mm2,eax
+ psllq mm2, 32
+ mov ax, bx
+ shl eax, 16
+ mov ax, bx
+ movd mm3,eax
+ por mm2,mm3
+
+ pmulhw mm2, mm1
+ movq [edi], mm2
+ add edi, 8
+
+ dec ecx
+ cmp ecx,0
+ jg mmxloop1
+ emms
+ }
+#else
+ atti386_partialProductOutput(tmplen, leftvol, rightvol, partialBuf, mixedBuf);
+#endif
+ return tmplen << 1;
+}
+
+int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
+ int tmplen = len >> 2;
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx, tmplen
+ mov esi, buf1
+ mov edi, buf2
+
+mixloop1:
+ movq mm1, [edi]
+ movq mm2, [esi]
+ paddw mm1,mm2
+ movq [esi],mm1
+ add edi,8
+ add esi,8
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop1
+ emms
+ }
+#else
+ atti386_mixBuffers(buf1, buf2, tmplen);
+#endif
+ return tmplen << 2;
+}
+
+
+int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
+ int tmplen = len >> 2;
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx, tmplen
+ mov esi, buf1
+ mov edi, buf2
+
+mixloop2:
+ movq mm1, [esi]
+ movq mm2, [edi]
+ movq mm3, mm1
+ pmulhw mm1, mm2
+ paddw mm1,mm3
+ movq [esi],mm1
+ add edi,8
+ add esi,8
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop2
+ emms
+ }
+#else
+ atti386_mixBuffersRingMix(buf1, buf2, tmplen);
+#endif
+ return tmplen << 2;
+}
+
+int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
+ int tmplen = len >> 2;
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx, tmplen
+ mov esi, buf1
+ mov edi, buf2
+
+mixloop3:
+ movq mm1, [esi]
+ movq mm2, [edi]
+ pmulhw mm1, mm2
+ movq [esi],mm1
+ add edi,8
+ add esi,8
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop3
+ emms
+ }
+#else
+ atti386_mixBuffersRing(buf1, buf2, tmplen);
+#endif
+ return tmplen << 2;
+}
+
+int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
+ int tmplen = (len >> 1);
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx, tmplen
+ mov ax,volume
+ shl eax,16
+ mov ax,volume
+ movd mm3,eax
+ movd mm2,eax
+ psllq mm3, 32
+ por mm3,mm2
+ mov esi, useBuf
+ mov edi, stream
+mixloop4:
+ movq mm1, [esi]
+ movq mm2, [edi]
+ pmulhw mm1, mm3
+ paddw mm1,mm2
+ movq [edi], mm1
+
+ add esi,8
+ add edi,8
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop4
+ emms
+ }
+#else
+ atti386_produceOutput1(tmplen, volume, useBuf, stream);
+#endif
+ return tmplen << 1;
+}
+
+#endif
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/i386.h b/engines/sci/sfx/softseq/mt32/i386.h
new file mode 100644
index 0000000000..e8644411cd
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/i386.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_I386_H
+#define MT32EMU_I386_H
+
+namespace MT32Emu {
+#ifdef MT32EMU_HAVE_X86
+
+// Function that detects the availablity of SSE SIMD instructions
+bool DetectSIMD();
+// Function that detects the availablity of 3DNow instructions
+bool Detect3DNow();
+
+float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr);
+float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr);
+float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr);
+
+#if MT32EMU_USE_MMX > 0
+int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf);
+int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len);
+int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len);
+int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len);
+int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume);
+#endif
+
+#endif
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/mt32_file.cpp b/engines/sci/sfx/softseq/mt32/mt32_file.cpp
new file mode 100644
index 0000000000..86cb29fd49
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/mt32_file.cpp
@@ -0,0 +1,112 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+ bool ANSIFile::open(const char *filename, OpenMode mode) {
+ const char *fmode;
+ if (mode == OpenMode_read) {
+ fmode = "rb";
+ } else {
+ fmode = "wb";
+ }
+ fp = fopen(filename, fmode);
+ return (fp != NULL);
+ }
+
+ void ANSIFile::close() {
+ fclose(fp);
+ }
+
+ size_t ANSIFile::read(void *in, size_t size) {
+ return fread(in, 1, size, fp);
+ }
+
+ bool ANSIFile::readLine(char *in, size_t size) {
+ return fgets(in, (int)size, fp) != NULL;
+ }
+
+ bool ANSIFile::readBit8u(Bit8u *in) {
+ int c = fgetc(fp);
+ if (c == EOF)
+ return false;
+ *in = (Bit8u)c;
+ return true;
+ }
+
+ bool File::readBit16u(Bit16u *in) {
+ Bit8u b[2];
+ if (read(&b[0], 2) != 2)
+ return false;
+ *in = ((b[0] << 8) | b[1]);
+ return true;
+ }
+
+ bool File::readBit32u(Bit32u *in) {
+ Bit8u b[4];
+ if (read(&b[0], 4) != 4)
+ return false;
+ *in = ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
+ return true;
+ }
+
+ size_t ANSIFile::write(const void *out, size_t size) {
+ return fwrite(out, 1, size, fp);
+ }
+
+ bool ANSIFile::writeBit8u(Bit8u out) {
+ return fputc(out, fp) != EOF;
+ }
+
+ bool File::writeBit16u(Bit16u out) {
+ if (!writeBit8u((Bit8u)((out & 0xFF00) >> 8))) {
+ return false;
+ }
+ if (!writeBit8u((Bit8u)(out & 0x00FF))) {
+ return false;
+ }
+ return true;
+ }
+
+ bool File::writeBit32u(Bit32u out) {
+ if (!writeBit8u((Bit8u)((out & 0xFF000000) >> 24))) {
+ return false;
+ }
+ if (!writeBit8u((Bit8u)((out & 0x00FF0000) >> 16))) {
+ return false;
+ }
+ if (!writeBit8u((Bit8u)((out & 0x0000FF00) >> 8))) {
+ return false;
+ }
+ if (!writeBit8u((Bit8u)(out & 0x000000FF))) {
+ return false;
+ }
+ return true;
+ }
+
+ bool ANSIFile::isEOF() {
+ return feof(fp) != 0;
+ }
+}
diff --git a/engines/sci/sfx/softseq/mt32/mt32_file.h b/engines/sci/sfx/softseq/mt32/mt32_file.h
new file mode 100644
index 0000000000..5f05c9e9ae
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/mt32_file.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_FILE_H
+#define MT32EMU_FILE_H
+
+#include <stdio.h>
+
+namespace MT32Emu {
+
+class File {
+public:
+ enum OpenMode {
+ OpenMode_read = 0,
+ OpenMode_write = 1
+ };
+ virtual ~File() {}
+ virtual void close() = 0;
+ virtual size_t read(void *in, size_t size) = 0;
+ virtual bool readLine(char *in, size_t size) = 0;
+ virtual bool readBit8u(Bit8u *in) = 0;
+ virtual bool readBit16u(Bit16u *in);
+ virtual bool readBit32u(Bit32u *in);
+ virtual size_t write(const void *out, size_t size) = 0;
+ virtual bool writeBit8u(Bit8u out) = 0;
+ // Note: May write a single byte to the file before failing
+ virtual bool writeBit16u(Bit16u out);
+ // Note: May write some (<4) bytes to the file before failing
+ virtual bool writeBit32u(Bit32u out);
+ virtual bool isEOF() = 0;
+};
+
+class ANSIFile: public File {
+private:
+ FILE *fp;
+public:
+ bool open(const char *filename, OpenMode mode);
+ void close();
+ size_t read(void *in, size_t size);
+ bool readLine(char *in, size_t size);
+ bool readBit8u(Bit8u *in);
+ size_t write(const void *out, size_t size);
+ bool writeBit8u(Bit8u out);
+ bool isEOF();
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/mt32emu.h b/engines/sci/sfx/softseq/mt32/mt32emu.h
new file mode 100644
index 0000000000..0aa4df7488
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/mt32emu.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_MT32EMU_H
+#define MT32EMU_MT32EMU_H
+
+// Debugging
+// Show the instruments played
+#define MT32EMU_MONITOR_INSTRUMENTS 1
+// Shows number of partials MT-32 is playing, and on which parts
+#define MT32EMU_MONITOR_PARTIALS 0
+// Determines how the waveform cache file is handled (must be regenerated after sampling rate change)
+#define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache
+//#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache
+//#define MT32EMU_WAVECACHEMODE 2 // Ignore existing cache, generate and save cache
+//#define MT32EMU_WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache
+
+// Configuration
+// The maximum number of partials playing simultaneously
+#define MT32EMU_MAX_PARTIALS 32
+// The maximum number of notes playing simultaneously per part.
+// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial.
+#define MT32EMU_MAX_POLY 32
+// This calculates the exact frequencies of notes as they are played, instead of offsetting from pre-cached semitones. Potentially very slow.
+#define MT32EMU_ACCURATENOTES 0
+
+#if (defined (_MSC_VER) && defined(_M_IX86))
+#define MT32EMU_HAVE_X86
+#elif defined(__GNUC__)
+#if __GNUC__ >= 3 && defined(__i386__)
+#define MT32EMU_HAVE_X86
+#endif
+#endif
+
+#ifdef MT32EMU_HAVE_X86
+#define MT32EMU_USE_MMX 1
+#else
+#define MT32EMU_USE_MMX 0
+#endif
+
+#include "freeverb.h"
+
+#include "structures.h"
+#include "i386.h"
+#include "mt32_file.h"
+#include "tables.h"
+#include "partial.h"
+#include "partialManager.h"
+#include "part.h"
+#include "synth.h"
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/part.cpp b/engines/sci/sfx/softseq/mt32/part.cpp
new file mode 100644
index 0000000000..b3d71bccca
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/part.cpp
@@ -0,0 +1,632 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <string.h>
+#include <math.h>
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+static const Bit8u PartialStruct[13] = {
+ 0, 0, 2, 2, 1, 3,
+ 3, 0, 3, 0, 2, 1, 3 };
+
+static const Bit8u PartialMixStruct[13] = {
+ 0, 1, 0, 1, 1, 0,
+ 1, 3, 3, 2, 2, 2, 2 };
+
+static const float floatKeyfollow[17] = {
+ -1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f,
+ 1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f,
+ 5.0f/4.0f, 3.0f/2.0f, 2.0f,
+ 1.0009765625f, 1.0048828125f
+};
+
+//FIXME:KG: Put this dpoly stuff somewhere better
+bool dpoly::isActive() const {
+ return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL;
+}
+
+Bit32u dpoly::getAge() const {
+ for (int i = 0; i < 4; i++) {
+ if (partials[i] != NULL) {
+ return partials[i]->age;
+ }
+ }
+ return 0;
+}
+
+RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) {
+ strcpy(name, "Rhythm");
+ rhythmTemp = &synth->mt32ram.rhythmSettings[0];
+ refresh();
+}
+
+Part::Part(Synth *useSynth, unsigned int usePartNum) {
+ this->synth = useSynth;
+ this->partNum = usePartNum;
+ patchCache[0].dirty = true;
+ holdpedal = false;
+ patchTemp = &synth->mt32ram.patchSettings[partNum];
+ if (usePartNum == 8) {
+ // Nasty hack for rhythm
+ timbreTemp = NULL;
+ } else {
+ sprintf(name, "Part %d", partNum + 1);
+ timbreTemp = &synth->mt32ram.timbreSettings[partNum];
+ }
+ currentInstr[0] = 0;
+ currentInstr[10] = 0;
+ expression = 127;
+ volumeMult = 0;
+ volumesetting.leftvol = 32767;
+ volumesetting.rightvol = 32767;
+ bend = 0.0f;
+ memset(polyTable,0,sizeof(polyTable));
+ memset(patchCache, 0, sizeof(patchCache));
+}
+
+void Part::setHoldPedal(bool pedalval) {
+ if (holdpedal && !pedalval) {
+ holdpedal = false;
+ stopPedalHold();
+ } else {
+ holdpedal = pedalval;
+ }
+}
+
+void RhythmPart::setBend(unsigned int midiBend) {
+ synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend);
+ return;
+}
+
+void Part::setBend(unsigned int midiBend) {
+ // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, centre 0.0 and max 1.0
+ if (midiBend <= 0x2000) {
+ bend = ((signed int)midiBend - 0x2000) / (float)0x2000;
+ } else {
+ bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF;
+ }
+ // Loop through all partials to update their bend
+ for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
+ for (int j = 0; j < 4; j++) {
+ if (polyTable[i].partials[j] != NULL) {
+ polyTable[i].partials[j]->setBend(bend);
+ }
+ }
+ }
+}
+
+void RhythmPart::setModulation(unsigned int midiModulation) {
+ synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation);
+}
+
+void Part::setModulation(unsigned int midiModulation) {
+ // Just a bloody guess, as always, before I get things figured out
+ for (int t = 0; t < 4; t++) {
+ if (patchCache[t].playPartial) {
+ int newrate = (patchCache[t].modsense * midiModulation) >> 7;
+ //patchCache[t].lfoperiod = lfotable[newrate];
+ patchCache[t].lfodepth = newrate;
+ //FIXME:KG: timbreTemp->partial[t].lfo.depth =
+ }
+ }
+}
+
+void RhythmPart::refresh() {
+ updateVolume();
+ // (Re-)cache all the mapped timbres ahead of time
+ for (unsigned int drumNum = 0; drumNum < synth->controlROMMap->rhythmSettingsCount; drumNum++) {
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 127) // 94 on MT-32
+ continue;
+ Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14...
+ // FIXME:KG: Panning cache should be backed up to partials using it, too
+ if (pan < 7) {
+ drumPan[drumNum].leftvol = pan * 4681;
+ drumPan[drumNum].rightvol = 32767;
+ } else {
+ drumPan[drumNum].rightvol = (14 - pan) * 4681;
+ drumPan[drumNum].leftvol = 32767;
+ }
+ PatchCache *cache = drumCache[drumNum];
+ backupCacheToPartials(cache);
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ cache[t].dirty = true;
+ cache[t].pitchShift = 0.0f;
+ cache[t].benderRange = 0.0f;
+ cache[t].pansetptr = &drumPan[drumNum];
+ cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0;
+ }
+ }
+}
+
+void Part::refresh() {
+ updateVolume();
+ backupCacheToPartials(patchCache);
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ patchCache[t].dirty = true;
+ patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f;
+ patchCache[t].benderRange = patchTemp->patch.benderRange;
+ patchCache[t].pansetptr = &volumesetting;
+ patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
+ }
+ memcpy(currentInstr, timbreTemp->common.name, 10);
+}
+
+const char *Part::getCurrentInstr() const {
+ return &currentInstr[0];
+}
+
+void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
+ for (int m = 0; m < 85; m++) {
+ if (rhythmTemp[m].timbre == absTimbreNum - 128)
+ drumCache[m][0].dirty = true;
+ }
+}
+
+void Part::refreshTimbre(unsigned int absTimbreNum) {
+ if (getAbsTimbreNum() == absTimbreNum) {
+ memcpy(currentInstr, timbreTemp->common.name, 10);
+ patchCache[0].dirty = true;
+ }
+}
+
+int Part::fixBiaslevel(int srcpnt, int *dir) {
+ int noteat = srcpnt & 0x3F;
+ int outnote;
+ if (srcpnt < 64)
+ *dir = 0;
+ else
+ *dir = 1;
+ outnote = 33 + noteat;
+ //synth->printDebug("Bias note %d, dir %d", outnote, *dir);
+
+ return outnote;
+}
+
+int Part::fixKeyfollow(int srckey) {
+ if (srckey>=0 && srckey<=16) {
+ int keyfix[17] = { -256*16, -128*16, -64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116};
+ return keyfix[srckey];
+ } else {
+ //LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey);
+ return 256;
+ }
+}
+
+void Part::abortPoly(dpoly *poly) {
+ if (!poly->isPlaying) {
+ return;
+ }
+ for (int i = 0; i < 4; i++) {
+ Partial *partial = poly->partials[i];
+ if (partial != NULL) {
+ partial->deactivate();
+ }
+ }
+ poly->isPlaying = false;
+}
+
+void Part::setPatch(const PatchParam *patch) {
+ patchTemp->patch = *patch;
+}
+
+void RhythmPart::setTimbre(TimbreParam * /*timbre*/) {
+ synth->printDebug("%s: Attempted to call setTimbre() - doesn't make sense for rhythm", name);
+}
+
+void Part::setTimbre(TimbreParam *timbre) {
+ *timbreTemp = *timbre;
+}
+
+unsigned int RhythmPart::getAbsTimbreNum() const {
+ synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm", name);
+ return 0;
+}
+
+unsigned int Part::getAbsTimbreNum() const {
+ return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
+}
+
+void RhythmPart::setProgram(unsigned int patchNum) {
+ synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum);
+}
+
+void Part::setProgram(unsigned int patchNum) {
+ setPatch(&synth->mt32ram.patches[patchNum]);
+ setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre);
+
+ refresh();
+
+ allSoundOff(); //FIXME:KG: Is this correct?
+}
+
+void Part::backupCacheToPartials(PatchCache cache[4]) {
+ // check if any partials are still playing with the old patch cache
+ // if so then duplicate the cached data from the part to the partial so that
+ // we can change the part's cache without affecting the partial.
+ // We delay this until now to avoid a copy operation with every note played
+ for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
+ for (int i = 0; i < 4; i++) {
+ Partial *partial = polyTable[m].partials[i];
+ if (partial != NULL && partial->patchCache == &cache[i]) {
+ partial->cachebackup = cache[i];
+ partial->patchCache = &partial->cachebackup;
+ }
+ }
+ }
+}
+
+void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
+ backupCacheToPartials(cache);
+ int partialCount = 0;
+ for (int t = 0; t < 4; t++) {
+ if (((timbre->common.pmute >> t) & 0x1) == 1) {
+ cache[t].playPartial = true;
+ partialCount++;
+ } else {
+ cache[t].playPartial = false;
+ continue;
+ }
+
+ // Calculate and cache common parameters
+
+ cache[t].pcm = timbre->partial[t].wg.pcmwave;
+ cache[t].useBender = (timbre->partial[t].wg.bender == 1);
+
+ switch (t) {
+ case 0:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
+ cache[t].structurePosition = 0;
+ cache[t].structurePair = 1;
+ break;
+ case 1:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
+ cache[t].structurePosition = 1;
+ cache[t].structurePair = 0;
+ break;
+ case 2:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
+ cache[t].structurePosition = 0;
+ cache[t].structurePair = 3;
+ break;
+ case 3:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
+ cache[t].structurePosition = 1;
+ cache[t].structurePair = 2;
+ break;
+ default:
+ break;
+ }
+
+ cache[t].waveform = timbre->partial[t].wg.waveform;
+ cache[t].pulsewidth = timbre->partial[t].wg.pulsewid;
+ cache[t].pwsens = timbre->partial[t].wg.pwvelo;
+ if (timbre->partial[t].wg.keyfollow > 16) {
+ synth->printDebug("Bad keyfollow value in timbre!");
+ cache[t].pitchKeyfollow = 1.0f;
+ } else {
+ cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow];
+ }
+
+ cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f;
+ cache[t].pitchEnv = timbre->partial[t].env;
+ cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f);
+ cache[t].pitchsustain = cache[t].pitchEnv.level[3];
+
+ // Calculate and cache TVA envelope stuff
+ cache[t].ampEnv = timbre->partial[t].tva;
+ cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f);
+
+ cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]);
+ cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1;
+ cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]);
+ cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2;
+ cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf;
+
+ // Calculate and cache filter stuff
+ cache[t].filtEnv = timbre->partial[t].tvf;
+ cache[t].filtkeyfollow = fixKeyfollow(cache[t].filtEnv.keyfollow);
+ cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27);
+ cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir);
+ cache[t].tvfblevel = cache[t].filtEnv.biaslevel;
+ cache[t].filtsustain = cache[t].filtEnv.envlevel[3];
+
+ // Calculate and cache LFO stuff
+ cache[t].lfodepth = timbre->partial[t].lfo.depth;
+ cache[t].lfoperiod = synth->tables.lfoPeriod[(int)timbre->partial[t].lfo.rate];
+ cache[t].lforate = timbre->partial[t].lfo.rate;
+ cache[t].modsense = timbre->partial[t].lfo.modsense;
+ }
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ cache[t].dirty = false;
+ cache[t].partialCount = partialCount;
+ cache[t].sustain = (timbre->common.nosustain == 0);
+ }
+ //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform);
+
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): Recached timbre", name, currentInstr);
+ for (int i = 0; i < 4; i++) {
+ synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform);
+ }
+#endif
+}
+
+const char *Part::getName() const {
+ return name;
+}
+
+void Part::updateVolume() {
+ volumeMult = synth->tables.volumeMult[patchTemp->outlevel * expression / 127];
+}
+
+int Part::getVolume() const {
+ // FIXME: Use the mappings for this in the control ROM
+ return patchTemp->outlevel * 127 / 100;
+}
+
+void Part::setVolume(int midiVolume) {
+ // FIXME: Use the mappings for this in the control ROM
+ patchTemp->outlevel = (Bit8u)(midiVolume * 100 / 127);
+ updateVolume();
+ synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume);
+}
+
+void Part::setExpression(int midiExpression) {
+ expression = midiExpression;
+ updateVolume();
+}
+
+void RhythmPart::setPan(unsigned int midiPan)
+{
+ // FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct?
+ synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan);
+}
+
+void Part::setPan(unsigned int midiPan) {
+ // FIXME:KG: Tweaked this a bit so that we have a left 100%, centre and right 100%
+ // (But this makes the range somewhat skewed)
+ // Check against the real thing
+ // NOTE: Panning is inverted compared to GM.
+ if (midiPan < 64) {
+ volumesetting.leftvol = (Bit16s)(midiPan * 512);
+ volumesetting.rightvol = 32767;
+ } else if (midiPan == 64) {
+ volumesetting.leftvol = 32767;
+ volumesetting.rightvol = 32767;
+ } else {
+ volumesetting.rightvol = (Bit16s)((127 - midiPan) * 520);
+ volumesetting.leftvol = 32767;
+ }
+ patchTemp->panpot = (Bit8u)(midiPan * 14 / 127);
+ //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
+}
+
+void RhythmPart::playNote(unsigned int key, int vel) {
+ if (key < 24 || key > 108)/*> 87 on MT-32)*/ {
+ synth->printDebug("%s: Attempted to play invalid key %d", name, key);
+ return;
+ }
+ int drumNum = key - 24;
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 127) { // 94 on MT-32
+ synth->printDebug("%s: Attempted to play unmapped key %d", name, key);
+ return;
+ }
+ int absTimbreNum = drumTimbreNum + 128;
+ TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre;
+ memcpy(currentInstr, timbre->common.name, 10);
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d", name, currentInstr, drumNum, absTimbreNum, vel, key);
+#endif
+ if (drumCache[drumNum][0].dirty) {
+ cacheTimbre(drumCache[drumNum], timbre);
+ }
+ playPoly(drumCache[drumNum], key, MIDDLEC, vel);
+}
+
+void Part::playNote(unsigned int key, int vel) {
+ int freqNum = key;
+ if (freqNum < 12) {
+ synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key);
+ freqNum += 12;
+ } else if (freqNum > 108) {
+ synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key);
+ while (freqNum > 108) {
+ freqNum -= 12;
+ }
+ }
+ // POLY1 mode, Single Assign
+ // Haven't found any software that uses any of the other poly modes
+ // FIXME:KG: Should this also apply to rhythm?
+ for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) {
+ if (polyTable[i].isActive() && (polyTable[i].key == key)) {
+ //AbortPoly(&polyTable[i]);
+ stopNote(key);
+ break;
+ }
+ }
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): starting poly - Vel %d Key %d", name, currentInstr, vel, key);
+#endif
+ if (patchCache[0].dirty) {
+ cacheTimbre(patchCache, timbreTemp);
+ }
+ playPoly(patchCache, key, freqNum, vel);
+}
+
+void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) {
+ unsigned int needPartials = cache[0].partialCount;
+ unsigned int freePartials = synth->partialManager->getFreePartialCount();
+
+ if (freePartials < needPartials) {
+ if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) {
+ synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount());
+ return;
+ }
+ }
+ // Find free poly
+ int m;
+ for (m = 0; m < MT32EMU_MAX_POLY; m++) {
+ if (!polyTable[m].isActive()) {
+ break;
+ }
+ }
+ if (m == MT32EMU_MAX_POLY) {
+ synth->printDebug("%s (%s): No free poly to play key %d (vel %d)", name, currentInstr, key, vel);
+ return;
+ }
+
+ dpoly *tpoly = &polyTable[m];
+
+ tpoly->isPlaying = true;
+ tpoly->key = key;
+ tpoly->isDecay = false;
+ tpoly->freqnum = freqNum;
+ tpoly->vel = vel;
+ tpoly->pedalhold = false;
+
+ bool allnull = true;
+ for (int x = 0; x < 4; x++) {
+ if (cache[x].playPartial) {
+ tpoly->partials[x] = synth->partialManager->allocPartial(partNum);
+ allnull = false;
+ } else {
+ tpoly->partials[x] = NULL;
+ }
+ }
+
+ if (allnull)
+ synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr);
+
+ tpoly->sustain = cache[0].sustain;
+ tpoly->volumeptr = &volumeMult;
+
+ for (int x = 0; x < 4; x++) {
+ if (tpoly->partials[x] != NULL) {
+ tpoly->partials[x]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]);
+ tpoly->partials[x]->setBend(bend);
+ }
+ }
+}
+
+static void startDecayPoly(dpoly *tpoly) {
+ if (tpoly->isDecay) {
+ return;
+ }
+ tpoly->isDecay = true;
+
+ for (int t = 0; t < 4; t++) {
+ Partial *partial = tpoly->partials[t];
+ if (partial == NULL)
+ continue;
+ partial->startDecayAll();
+ }
+ tpoly->isPlaying = false;
+}
+
+void Part::allNotesOff() {
+ // Note: Unchecked on real MT-32, but the MIDI specification states that all notes off (0x7B)
+ // should treat the hold pedal as usual.
+ // All *sound* off (0x78) should stop notes immediately regardless of the hold pedal.
+ // The latter controller is not implemented on the MT-32 (according to the docs).
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+ if (tpoly->isPlaying) {
+ if (holdpedal)
+ tpoly->pedalhold = true;
+ else if (tpoly->sustain)
+ startDecayPoly(tpoly);
+ }
+ }
+}
+
+void Part::allSoundOff() {
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+ if (tpoly->isPlaying) {
+ startDecayPoly(tpoly);
+ }
+ }
+}
+
+void Part::stopPedalHold() {
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly;
+ tpoly = &polyTable[q];
+ if (tpoly->isActive() && tpoly->pedalhold)
+ stopNote(tpoly->key);
+ }
+}
+
+void Part::stopNote(unsigned int key) {
+ // Non-sustaining instruments ignore stop commands.
+ // They die away eventually anyway
+
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key);
+#endif
+
+ if (key != 255) {
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+ if (tpoly->isPlaying && tpoly->key == key) {
+ if (holdpedal)
+ tpoly->pedalhold = true;
+ else if (tpoly->sustain)
+ startDecayPoly(tpoly);
+ }
+ }
+ return;
+ }
+
+ // Find oldest poly... yes, the MT-32 can be reconfigured to kill different poly first
+ // This is simplest
+ int oldest = -1;
+ Bit32u oldage = 0;
+
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+
+ if (tpoly->isPlaying && !tpoly->isDecay) {
+ if (tpoly->getAge() >= oldage) {
+ oldage = tpoly->getAge();
+ oldest = q;
+ }
+ }
+ }
+
+ if (oldest != -1) {
+ startDecayPoly(&polyTable[oldest]);
+ }
+}
+
+}
diff --git a/engines/sci/sfx/softseq/mt32/part.h b/engines/sci/sfx/softseq/mt32/part.h
new file mode 100644
index 0000000000..54c4999653
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/part.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_PART_H
+#define MT32EMU_PART_H
+
+namespace MT32Emu {
+
+class PartialManager;
+class Synth;
+
+class Part {
+private:
+ // Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)
+ MemParams::PatchTemp *patchTemp;
+ TimbreParam *timbreTemp;
+
+ // 0=Part 1, .. 7=Part 8, 8=Rhythm
+ unsigned int partNum;
+
+ bool holdpedal;
+
+ StereoVolume volumesetting;
+
+ PatchCache patchCache[4];
+
+ float bend; // -1.0 .. +1.0
+
+ dpoly polyTable[MT32EMU_MAX_POLY];
+
+ void abortPoly(dpoly *poly);
+
+ static int fixKeyfollow(int srckey);
+ static int fixBiaslevel(int srcpnt, int *dir);
+
+ void setPatch(const PatchParam *patch);
+
+protected:
+ Synth *synth;
+ char name[8]; // "Part 1".."Part 8", "Rhythm"
+ char currentInstr[11];
+ int expression;
+ Bit32u volumeMult;
+
+ void updateVolume();
+ void backupCacheToPartials(PatchCache cache[4]);
+ void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre);
+ void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel);
+ const char *getName() const;
+
+public:
+ Part(Synth *synth, unsigned int usePartNum);
+ virtual ~Part() {}
+ virtual void playNote(unsigned int key, int vel);
+ void stopNote(unsigned int key);
+ void allNotesOff();
+ void allSoundOff();
+ int getVolume() const;
+ void setVolume(int midiVolume);
+ void setExpression(int midiExpression);
+ virtual void setPan(unsigned int midiPan);
+ virtual void setBend(unsigned int midiBend);
+ virtual void setModulation(unsigned int midiModulation);
+ virtual void setProgram(unsigned int midiProgram);
+ void setHoldPedal(bool pedalval);
+ void stopPedalHold();
+ virtual void refresh();
+ virtual void refreshTimbre(unsigned int absTimbreNum);
+ virtual void setTimbre(TimbreParam *timbre);
+ virtual unsigned int getAbsTimbreNum() const;
+ const char *getCurrentInstr() const;
+};
+
+class RhythmPart: public Part {
+ // Pointer to the area of the MT-32's memory dedicated to rhythm
+ const MemParams::RhythmTemp *rhythmTemp;
+
+ // This caches the timbres/settings in use by the rhythm part
+ PatchCache drumCache[85][4];
+ StereoVolume drumPan[85];
+public:
+ RhythmPart(Synth *synth, unsigned int usePartNum);
+ void refresh();
+ void refreshTimbre(unsigned int timbreNum);
+ void setTimbre(TimbreParam *timbre);
+ void playNote(unsigned int key, int vel);
+ unsigned int getAbsTimbreNum() const;
+ void setPan(unsigned int midiPan);
+ void setBend(unsigned int midiBend);
+ void setModulation(unsigned int midiModulation);
+ void setProgram(unsigned int patchNum);
+};
+
+}
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/partial.cpp b/engines/sci/sfx/softseq/mt32/partial.cpp
new file mode 100644
index 0000000000..9d32282a82
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/partial.cpp
@@ -0,0 +1,960 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mt32emu.h"
+
+#ifdef MACOSX
+// Older versions of Mac OS X didn't supply a powf function. To ensure
+// binary compatibility, we force using pow instead of powf (the only
+// potential drawback is that it might be a little bit slower).
+#define powf pow
+#endif
+
+#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y)))
+#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y)))
+#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point)
+#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point))
+
+using namespace MT32Emu;
+
+Partial::Partial(Synth *useSynth) {
+ this->synth = useSynth;
+ ownerPart = -1;
+ poly = NULL;
+ pair = NULL;
+#if MT32EMU_ACCURATENOTES == 1
+ for (int i = 0; i < 3; i++) {
+ noteLookupStorage.waveforms[i] = new Bit16s[65536];
+ }
+ noteLookup = &noteLookupStorage;
+#endif
+}
+
+Partial::~Partial() {
+#if MT32EMU_ACCURATENOTES == 1
+ for (int i = 0; i < 3; i++) {
+ delete[] noteLookupStorage.waveforms[i];
+ }
+ delete[] noteLookupStorage.wavTable;
+#endif
+}
+
+int Partial::getOwnerPart() const {
+ return ownerPart;
+}
+
+bool Partial::isActive() {
+ return ownerPart > -1;
+}
+
+const dpoly *Partial::getDpoly() const {
+ return this->poly;
+}
+
+void Partial::activate(int part) {
+ // This just marks the partial as being assigned to a part
+ ownerPart = part;
+}
+
+void Partial::deactivate() {
+ ownerPart = -1;
+ if (poly != NULL) {
+ for (int i = 0; i < 4; i++) {
+ if (poly->partials[i] == this) {
+ poly->partials[i] = NULL;
+ break;
+ }
+ }
+ if (pair != NULL) {
+ pair->pair = NULL;
+ }
+ }
+}
+
+void Partial::initKeyFollow(int key) {
+ // Setup partial keyfollow
+ // Note follow relative to middle C
+
+ // Calculate keyfollow for pitch
+#if 1
+ float rel = key == -1 ? 0.0f : (key - MIDDLEC);
+ float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift;
+ //FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)?
+ //int newKey = (int)(rel * patchCache->pitchKeyfollow);
+ //float newPitch = newKey + patchCache->pitch + patchCache->pitchShift;
+#else
+ float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC);
+ float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch;
+#endif
+#if MT32EMU_ACCURATENOTES == 1
+ noteVal = newPitch;
+ synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch);
+#else
+ float newPitchInt;
+ float newPitchFract = modff(newPitch, &newPitchInt);
+ if (newPitchFract > 0.5f) {
+ newPitchInt += 1.0f;
+ newPitchFract -= 1.0f;
+ }
+ noteVal = (int)newPitchInt;
+ fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f);
+ synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch, noteVal, fineShift);
+#endif
+ // FIXME:KG: Raise/lower by octaves until in the supported range.
+ while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108?
+ noteVal -= 12;
+ while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12?
+ noteVal += 12;
+ // Calculate keyfollow for filter
+ int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096;
+ if (keyfollow > 108)
+ keyfollow = 108;
+ else if (keyfollow < -108)
+ keyfollow = -108;
+ filtVal = synth->tables.tvfKeyfollowMult[keyfollow + 108];
+ realVal = synth->tables.tvfKeyfollowMult[(noteVal - MIDDLEC) + 108];
+}
+
+int Partial::getKey() const {
+ if (poly == NULL) {
+ return -1;
+ } else {
+ return poly->key;
+ }
+}
+
+void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) {
+ if (usePoly == NULL || useCache == NULL) {
+ synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK");
+ return;
+ }
+ patchCache = useCache;
+ poly = usePoly;
+ mixType = patchCache->structureMix;
+ structurePosition = patchCache->structurePosition;
+
+ play = true;
+ initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal
+#if MT32EMU_ACCURATENOTES == 0
+ noteLookup = &synth->tables.noteLookups[noteVal - LOWEST_NOTE];
+#else
+ Tables::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->pcmWaves, NULL);
+#endif
+ keyLookup = &synth->tables.keyLookups[poly->freqnum - 12];
+
+ if (patchCache->PCMPartial) {
+ pcmNum = patchCache->pcm;
+ if (synth->controlROMMap->pcmCount > 128) {
+ // CM-32L, etc. support two "banks" of PCMs, selectable by waveform type parameter.
+ if (patchCache->waveform > 1) {
+ pcmNum += 128;
+ }
+ }
+ pcmWave = &synth->pcmWaves[pcmNum];
+ } else {
+ pcmWave = NULL;
+ }
+
+ lfoPos = 0;
+ pulsewidth = patchCache->pulsewidth + synth->tables.pwVelfollowAdd[patchCache->pwsens][poly->vel];
+ if (pulsewidth > 100) {
+ pulsewidth = 100;
+ } else if (pulsewidth < 0) {
+ pulsewidth = 0;
+ }
+
+ for (int e = 0; e < 3; e++) {
+ envs[e].envpos = 0;
+ envs[e].envstat = -1;
+ envs[e].envbase = 0;
+ envs[e].envdist = 0;
+ envs[e].envsize = 0;
+ envs[e].sustaining = false;
+ envs[e].decaying = false;
+ envs[e].prevlevel = 0;
+ envs[e].counter = 0;
+ envs[e].count = 0;
+ }
+ ampEnvVal = 0;
+ pitchEnvVal = 0;
+ pitchSustain = false;
+ loopPos = 0;
+ partialOff.pcmoffset = partialOff.pcmplace = 0;
+ pair = pairPartial;
+ useNoisePair = pairPartial == NULL && (mixType == 1 || mixType == 2);
+ age = 0;
+ alreadyOutputed = false;
+ memset(history,0,sizeof(history));
+}
+
+Bit16s *Partial::generateSamples(long length) {
+ if (!isActive() || alreadyOutputed) {
+ return NULL;
+ }
+ if (poly == NULL) {
+ synth->printDebug("*** ERROR: poly is NULL at Partial::generateSamples()!");
+ return NULL;
+ }
+
+ alreadyOutputed = true;
+
+ // Generate samples
+
+ Bit16s *partialBuf = &myBuffer[0];
+ Bit32u volume = *poly->volumeptr;
+ while (length--) {
+ Bit32s envval;
+ Bit32s sample = 0;
+ if (!envs[EnvelopeType_amp].sustaining) {
+ if (envs[EnvelopeType_amp].count <= 0) {
+ Bit32u ampval = getAmpEnvelope();
+ if (!play) {
+ deactivate();
+ break;
+ }
+ if (ampval > 100) {
+ ampval = 100;
+ }
+
+ ampval = synth->tables.volumeMult[ampval];
+ ampval = FIXEDPOINT_UMULT(ampval, synth->tables.tvaVelfollowMult[poly->vel][(int)patchCache->ampEnv.velosens], 8);
+ //if (envs[EnvelopeType_amp].sustaining)
+ ampEnvVal = ampval;
+ }
+ --envs[EnvelopeType_amp].count;
+ }
+
+ unsigned int lfoShift = 0x1000;
+ if (pitchSustain) {
+ // Calculate LFO position
+ // LFO does not kick in completely until pitch envelope sustains
+ if (patchCache->lfodepth > 0) {
+ lfoPos++;
+ if (lfoPos >= patchCache->lfoperiod)
+ lfoPos = 0;
+ int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16);
+ int lfoatr = synth->tables.sintable[lfoatm];
+ lfoShift = synth->tables.lfoShift[patchCache->lfodepth][lfoatr];
+ }
+ } else {
+ // Calculate Pitch envelope
+ envval = getPitchEnvelope();
+ int pd = patchCache->pitchEnv.depth;
+ pitchEnvVal = synth->tables.pitchEnvVal[pd][envval];
+ }
+
+ int delta;
+
+ // Wrap positions or end if necessary
+ if (patchCache->PCMPartial) {
+ // PCM partial
+
+ delta = noteLookup->wavTable[pcmNum];
+ int len = pcmWave->len;
+ if (partialOff.pcmplace >= len) {
+ if (pcmWave->loop) {
+ //partialOff.pcmplace = partialOff.pcmoffset = 0;
+ partialOff.pcmplace %= len;
+ } else {
+ play = false;
+ deactivate();
+ break;
+ }
+ }
+ } else {
+ // Synthesis partial
+ delta = 0x10000;
+ partialOff.pcmplace %= (Bit16u)noteLookup->div2;
+ }
+
+ // Build delta for position of next sample
+ // Fix delta code
+ Bit32u tdelta = delta;
+#if MT32EMU_ACCURATENOTES == 0
+ tdelta = FIXEDPOINT_UMULT(tdelta, fineShift, 12);
+#endif
+ tdelta = FIXEDPOINT_UMULT(tdelta, pitchEnvVal, 12);
+ tdelta = FIXEDPOINT_UMULT(tdelta, lfoShift, 12);
+ tdelta = FIXEDPOINT_UMULT(tdelta, bendShift, 12);
+ delta = (int)tdelta;
+
+ // Get waveform - either PCM or synthesized sawtooth or square
+ if (ampEnvVal > 0) {
+ if (patchCache->PCMPartial) {
+ // Render PCM sample
+ int ra, rb, dist;
+ Bit32u taddr;
+ Bit32u pcmAddr = pcmWave->addr;
+ if (delta < 0x10000) {
+ // Linear sound interpolation
+ taddr = pcmAddr + partialOff.pcmplace;
+ ra = synth->pcmROMData[taddr];
+ taddr++;
+ if (taddr == pcmAddr + pcmWave->len) {
+ // Past end of PCM
+ if (pcmWave->loop) {
+ rb = synth->pcmROMData[pcmAddr];
+ } else {
+ rb = 0;
+ }
+ } else {
+ rb = synth->pcmROMData[taddr];
+ }
+ dist = rb - ra;
+ sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8));
+ } else {
+ // Sound decimation
+ // The right way to do it is to use a lowpass filter on the waveform before selecting
+ // a point. This is too slow. The following approximates this as fast as possible
+ int idelta = delta >> 16;
+ taddr = pcmAddr + partialOff.pcmplace;
+ ra = synth->pcmROMData[taddr++];
+ for (int ix = 0; ix < idelta - 1; ix++) {
+ if (taddr == pcmAddr + pcmWave->len) {
+ // Past end of PCM
+ if (pcmWave->loop) {
+ taddr = pcmAddr;
+ } else {
+ // Behave as if all subsequent samples were 0
+ break;
+ }
+ }
+ ra += synth->pcmROMData[taddr++];
+ }
+ sample = ra / idelta;
+ }
+ } else {
+ // Render synthesised sample
+ int toff = partialOff.pcmplace;
+ int minorplace = partialOff.pcmoffset >> 14;
+ Bit32s filterInput;
+ Bit32s filtval = getFiltEnvelope();
+
+ //synth->printDebug("Filtval: %d", filtval);
+
+ if ((patchCache->waveform & 1) == 0) {
+ // Square waveform. Made by combining two pregenerated bandlimited
+ // sawtooth waveforms
+ Bit32u ofsA = ((toff << 2) + minorplace) % noteLookup->waveformSize[0];
+ int width = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[pulsewidth], 7);
+ Bit32u ofsB = (ofsA + width) % noteLookup->waveformSize[0];
+ Bit16s pa = noteLookup->waveforms[0][ofsA];
+ Bit16s pb = noteLookup->waveforms[0][ofsB];
+ filterInput = pa - pb;
+ // Non-bandlimited squarewave
+ /*
+ ofs = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 8);
+ if (toff < ofs)
+ sample = 1 * WGAMP;
+ else
+ sample = -1 * WGAMP;
+ */
+ } else {
+ // Sawtooth. Made by combining the full cosine and half cosine according
+ // to how it looks on the MT-32. What it really does it takes the
+ // square wave and multiplies it by a full cosine
+ int waveoff = (toff << 2) + minorplace;
+ if (toff < noteLookup->sawTable[pulsewidth])
+ filterInput = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
+ else
+ filterInput = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
+ // This is the correct way
+ // Seems slow to me (though bandlimited) -- doesn't seem to
+ // sound any better though
+ /*
+ //int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8;
+
+ Bit32u ofs = toff % (noteLookup->div2 >> 1);
+
+ Bit32u ofs3 = toff + FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 9);
+ ofs3 = ofs3 % (noteLookup->div2 >> 1);
+
+ pa = noteLookup->waveforms[0][ofs];
+ pb = noteLookup->waveforms[0][ofs3];
+ sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / 2;
+ */
+ }
+
+ //Very exact filter
+ if (filtval > ((FILTERGRAN * 15) / 16))
+ filtval = ((FILTERGRAN * 15) / 16);
+ sample = (Bit32s)(floorf((synth->iirFilter)((float)filterInput, &history[0], synth->tables.filtCoeff[filtval][(int)patchCache->filtEnv.resonance])) / synth->tables.resonanceFactor[patchCache->filtEnv.resonance]);
+ if (sample < -32768) {
+ synth->printDebug("Overdriven amplitude for %d: %d:=%d < -32768", patchCache->waveform, filterInput, sample);
+ sample = -32768;
+ }
+ else if (sample > 32767) {
+ synth->printDebug("Overdriven amplitude for %d: %d:=%d > 32767", patchCache->waveform, filterInput, sample);
+ sample = 32767;
+ }
+ }
+ }
+
+ // Add calculated delta to our waveform offset
+ Bit32u absOff = ((partialOff.pcmplace << 16) | partialOff.pcmoffset);
+ absOff += delta;
+ partialOff.pcmplace = (Bit16u)((absOff & 0xFFFF0000) >> 16);
+ partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF);
+
+ // Put volume envelope over generated sample
+ sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9);
+ sample = FIXEDPOINT_SMULT(sample, volume, 7);
+ envs[EnvelopeType_amp].envpos++;
+ envs[EnvelopeType_pitch].envpos++;
+ envs[EnvelopeType_filt].envpos++;
+
+ *partialBuf++ = (Bit16s)sample;
+ }
+ // We may have deactivated and broken out of the loop before the end of the buffer,
+ // if so then fill the remainder with 0s.
+ if (++length > 0)
+ memset(partialBuf, 0, length * 2);
+ return &myBuffer[0];
+}
+
+void Partial::setBend(float factor) {
+ if (!patchCache->useBender || factor == 0.0f) {
+ bendShift = 4096;
+ return;
+ }
+ // NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB.
+ // FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs.
+ float bendSemitones = factor * patchCache->benderRange; // -24 .. 24
+ float mult = powf(2.0f, bendSemitones / 12.0f);
+ synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", factor, patchCache->benderRange, bendSemitones, mult);
+ bendShift = (int)(mult * 4096.0f);
+}
+
+Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
+ if (buf1 == NULL)
+ return buf2;
+ if (buf2 == NULL)
+ return buf1;
+
+ Bit16s *outBuf = buf1;
+#if MT32EMU_USE_MMX >= 1
+ // KG: This seems to be fine
+ int donelen = i386_mixBuffers(buf1, buf2, len);
+ len -= donelen;
+ buf1 += donelen;
+ buf2 += donelen;
+#endif
+ while (len--) {
+ *buf1 = *buf1 + *buf2;
+ buf1++, buf2++;
+ }
+ return outBuf;
+}
+
+Bit16s *Partial::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
+ if (buf1 == NULL)
+ return NULL;
+ if (buf2 == NULL) {
+ Bit16s *outBuf = buf1;
+ while (len--) {
+ if (*buf1 < -8192)
+ *buf1 = -8192;
+ else if (*buf1 > 8192)
+ *buf1 = 8192;
+ buf1++;
+ }
+ return outBuf;
+ }
+
+ Bit16s *outBuf = buf1;
+#if MT32EMU_USE_MMX >= 1
+ // KG: This seems to be fine
+ int donelen = i386_mixBuffersRingMix(buf1, buf2, len);
+ len -= donelen;
+ buf1 += donelen;
+ buf2 += donelen;
+#endif
+ while (len--) {
+ float a, b;
+ a = ((float)*buf1) / 8192.0f;
+ b = ((float)*buf2) / 8192.0f;
+ a = (a * b) + a;
+ if (a>1.0)
+ a = 1.0;
+ if (a<-1.0)
+ a = -1.0;
+ *buf1 = (Bit16s)(a * 8192.0f);
+ buf1++;
+ buf2++;
+ //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i];
+ }
+ return outBuf;
+}
+
+Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
+ if (buf1 == NULL) {
+ return NULL;
+ }
+ if (buf2 == NULL) {
+ return NULL;
+ }
+
+ Bit16s *outBuf = buf1;
+#if MT32EMU_USE_MMX >= 1
+ // FIXME:KG: Not really checked as working
+ int donelen = i386_mixBuffersRing(buf1, buf2, len);
+ len -= donelen;
+ buf1 += donelen;
+ buf2 += donelen;
+#endif
+ while (len--) {
+ float a, b;
+ a = ((float)*buf1) / 8192.0f;
+ b = ((float)*buf2) / 8192.0f;
+ a *= b;
+ if (a>1.0)
+ a = 1.0;
+ if (a<-1.0)
+ a = -1.0;
+ *buf1 = (Bit16s)(a * 8192.0f);
+ buf1++;
+ buf2++;
+ }
+ return outBuf;
+}
+
+void Partial::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) {
+ if (buf2 == NULL) {
+ while (len--) {
+ *outBuf++ = *buf1++;
+ *outBuf++ = 0;
+ }
+ } else if (buf1 == NULL) {
+ while (len--) {
+ *outBuf++ = 0;
+ *outBuf++ = *buf2++;
+ }
+ } else {
+ while (len--) {
+ *outBuf++ = *buf1++;
+ *outBuf++ = *buf2++;
+ }
+ }
+}
+
+bool Partial::produceOutput(Bit16s *partialBuf, long length) {
+ if (!isActive() || alreadyOutputed)
+ return false;
+ if (poly == NULL) {
+ synth->printDebug("*** ERROR: poly is NULL at Partial::produceOutput()!");
+ return false;
+ }
+
+ Bit16s *pairBuf = NULL;
+ // Check for dependant partial
+ if (pair != NULL) {
+ if (!pair->alreadyOutputed) {
+ // Note: pair may have become NULL after this
+ pairBuf = pair->generateSamples(length);
+ }
+ } else if (useNoisePair) {
+ // Generate noise for pairless ring mix
+ pairBuf = synth->tables.noiseBuf;
+ }
+
+ Bit16s *myBuf = generateSamples(length);
+
+ if (myBuf == NULL && pairBuf == NULL)
+ return false;
+
+ Bit16s *p1buf, *p2buf;
+
+ if (structurePosition == 0 || pairBuf == NULL) {
+ p1buf = myBuf;
+ p2buf = pairBuf;
+ } else {
+ p2buf = myBuf;
+ p1buf = pairBuf;
+ }
+
+ //synth->printDebug("mixType: %d", mixType);
+
+ Bit16s *mixedBuf;
+ switch (mixType) {
+ case 0:
+ // Standard sound mix
+ mixedBuf = mixBuffers(p1buf, p2buf, length);
+ break;
+
+ case 1:
+ // Ring modulation with sound mix
+ mixedBuf = mixBuffersRingMix(p1buf, p2buf, length);
+ break;
+
+ case 2:
+ // Ring modulation alone
+ mixedBuf = mixBuffersRing(p1buf, p2buf, length);
+ break;
+
+ case 3:
+ // Stereo mixing. One partial to one speaker channel, one to another.
+ // FIXME:KG: Surely we should be multiplying by the left/right volumes here?
+ mixBuffersStereo(p1buf, p2buf, partialBuf, length);
+ return true;
+
+ default:
+ mixedBuf = mixBuffers(p1buf, p2buf, length);
+ break;
+ }
+
+ if (mixedBuf == NULL)
+ return false;
+
+ Bit16s leftvol, rightvol;
+ leftvol = patchCache->pansetptr->leftvol;
+ rightvol = patchCache->pansetptr->rightvol;
+
+#if MT32EMU_USE_MMX >= 2
+ // FIXME:KG: This appears to introduce crackle
+ int donelen = i386_partialProductOutput(length, leftvol, rightvol, partialBuf, mixedBuf);
+ length -= donelen;
+ mixedBuf += donelen;
+ partialBuf += donelen * 2;
+#endif
+ while (length--) {
+ *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)leftvol) >> 15);
+ *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)rightvol) >> 15);
+ mixedBuf++;
+ }
+ return true;
+}
+
+Bit32s Partial::getFiltEnvelope() {
+ int reshigh;
+
+ int cutoff, depth;
+
+ EnvelopeStatus *tStat = &envs[EnvelopeType_filt];
+
+ if (tStat->decaying) {
+ reshigh = tStat->envbase;
+ reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ if (tStat->envpos >= tStat->envsize)
+ reshigh = 0;
+ } else {
+ if (tStat->envstat==4) {
+ reshigh = patchCache->filtsustain;
+ if (!poly->sustain) {
+ startDecay(EnvelopeType_filt, reshigh);
+ }
+ } else {
+ if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
+ if (tStat->envstat==-1)
+ tStat->envbase = 0;
+ else
+ tStat->envbase = patchCache->filtEnv.envlevel[tStat->envstat];
+ tStat->envstat++;
+ tStat->envpos = 0;
+ if (tStat->envstat == 3) {
+ tStat->envsize = synth->tables.envTime[(int)patchCache->filtEnv.envtime[tStat->envstat]];
+ } else {
+ Bit32u envTime = (int)patchCache->filtEnv.envtime[tStat->envstat];
+ if (tStat->envstat > 1) {
+ int envDiff = abs(patchCache->filtEnv.envlevel[tStat->envstat] - patchCache->filtEnv.envlevel[tStat->envstat - 1]);
+ if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
+ envTime = synth->tables.envDeltaMaxTime[envDiff];
+ }
+ }
+
+ tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf]) >> 8;
+ }
+
+ tStat->envsize++;
+ tStat->envdist = patchCache->filtEnv.envlevel[tStat->envstat] - tStat->envbase;
+ }
+
+ reshigh = tStat->envbase;
+ reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+
+ }
+ tStat->prevlevel = reshigh;
+ }
+
+ cutoff = patchCache->filtEnv.cutoff;
+
+ //if (patchCache->waveform==1) reshigh = (reshigh * 3) >> 2;
+
+ depth = patchCache->filtEnv.envdepth;
+
+ //int sensedep = (depth * 127-patchCache->filtEnv.envsense) >> 7;
+ depth = FIXEDPOINT_UMULT(depth, synth->tables.tvfVelfollowMult[poly->vel][(int)patchCache->filtEnv.envsense], 8);
+
+ int bias = patchCache->tvfbias;
+ int dist;
+
+ if (bias != 0) {
+ //FIXME:KG: Is this really based on pitch (as now), or key pressed?
+ //synth->printDebug("Cutoff before %d", cutoff);
+ if (patchCache->tvfdir == 0) {
+ if (noteVal < bias) {
+ dist = bias - noteVal;
+ cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
+ }
+ } else {
+ // > Bias
+ if (noteVal > bias) {
+ dist = noteVal - bias;
+ cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
+ }
+
+ }
+ //synth->printDebug("Cutoff after %d", cutoff);
+ }
+
+ depth = (depth * keyLookup->envDepthMult[patchCache->filtEnv.envdkf]) >> 8;
+ reshigh = (reshigh * depth) >> 7;
+
+ Bit32s tmp;
+
+ cutoff *= filtVal;
+ cutoff /= realVal; //FIXME:KG: With filter keyfollow 0, this makes no sense. What's correct?
+
+ reshigh *= filtVal;
+ reshigh /= realVal; //FIXME:KG: As above for cutoff
+
+ if (patchCache->waveform == 1) {
+ reshigh = (reshigh * 65) / 100;
+ }
+
+ if (cutoff > 100)
+ cutoff = 100;
+ else if (cutoff < 0)
+ cutoff = 0;
+ if (reshigh > 100)
+ reshigh = 100;
+ else if (reshigh < 0)
+ reshigh = 0;
+ tmp = noteLookup->nfiltTable[cutoff][reshigh];
+ //tmp *= keyfollow;
+ //tmp /= realfollow;
+
+ //synth->printDebug("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256);
+ return tmp;
+}
+
+bool Partial::shouldReverb() {
+ if (!isActive())
+ return false;
+ return patchCache->reverb;
+}
+
+Bit32u Partial::getAmpEnvelope() {
+ Bit32s tc;
+
+ EnvelopeStatus *tStat = &envs[EnvelopeType_amp];
+
+ if (!play)
+ return 0;
+
+ if (tStat->decaying) {
+ tc = tStat->envbase;
+ tc += (tStat->envdist * tStat->envpos) / tStat->envsize;
+ if (tc < 0)
+ tc = 0;
+ if ((tStat->envpos >= tStat->envsize) || (tc == 0)) {
+ play = false;
+ // Don't have to worry about prevlevel storage or anything, this partial's about to die
+ return 0;
+ }
+ } else {
+ if ((tStat->envstat == -1) || (tStat->envpos >= tStat->envsize)) {
+ if (tStat->envstat == -1)
+ tStat->envbase = 0;
+ else
+ tStat->envbase = patchCache->ampEnv.envlevel[tStat->envstat];
+ tStat->envstat++;
+ tStat->envpos = 0;
+ if (tStat->envstat == 4) {
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ tc = patchCache->ampEnv.envlevel[3];
+ if (!poly->sustain)
+ startDecay(EnvelopeType_amp, tc);
+ else
+ tStat->sustaining = true;
+ goto PastCalc;
+ }
+ Bit8u targetLevel = patchCache->ampEnv.envlevel[tStat->envstat];
+ tStat->envdist = targetLevel - tStat->envbase;
+ Bit32u envTime = patchCache->ampEnv.envtime[tStat->envstat];
+ if (targetLevel == 0) {
+ tStat->envsize = synth->tables.envDecayTime[envTime];
+ } else {
+ int envLevelDelta = abs(tStat->envdist);
+ if (envTime > synth->tables.envDeltaMaxTime[envLevelDelta]) {
+ envTime = synth->tables.envDeltaMaxTime[envLevelDelta];
+ }
+ tStat->envsize = synth->tables.envTime[envTime];
+ }
+
+ // Time keyfollow is used by all sections of the envelope (confirmed on CM-32L)
+ tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
+
+ switch (tStat->envstat) {
+ case 0:
+ //Spot for velocity time follow
+ //Only used for first attack
+ tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, synth->tables.envTimeVelfollowMult[(int)patchCache->ampEnv.envvkf][poly->vel], 8);
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ break;
+ default:
+ synth->printDebug("Invalid TVA envelope number %d hit!", tStat->envstat);
+ break;
+ }
+
+ tStat->envsize++;
+
+ if (tStat->envdist != 0) {
+ tStat->counter = abs(tStat->envsize / tStat->envdist);
+ //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
+ } else {
+ tStat->counter = 0;
+ //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
+ }
+ }
+ tc = tStat->envbase;
+ tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ tStat->count = tStat->counter;
+PastCalc:
+ tc = (tc * (Bit32s)patchCache->ampEnv.level) / 100;
+ }
+
+ // Prevlevel storage is bottle neck
+ tStat->prevlevel = tc;
+
+ //Bias level crap stuff now
+
+ for (int i = 0; i < 2; i++) {
+ if (patchCache->ampblevel[i]!=0) {
+ int bias = patchCache->ampbias[i];
+ if (patchCache->ampdir[i]==0) {
+ // < Bias
+ if (noteVal < bias) {
+ int dist = bias - noteVal;
+ tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
+ }
+ } else {
+ // > Bias
+ if (noteVal > bias) {
+ int dist = noteVal - bias;
+ tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
+ }
+ }
+ }
+ }
+ if (tc < 0) {
+ synth->printDebug("*** ERROR: tc < 0 (%d) at getAmpEnvelope()", tc);
+ tc = 0;
+ }
+ return (Bit32u)tc;
+}
+
+Bit32s Partial::getPitchEnvelope() {
+ EnvelopeStatus *tStat = &envs[EnvelopeType_pitch];
+
+ Bit32s tc;
+ pitchSustain = false;
+ if (tStat->decaying) {
+ if (tStat->envpos >= tStat->envsize)
+ tc = patchCache->pitchEnv.level[4];
+ else {
+ tc = tStat->envbase;
+ tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ }
+ } else {
+ if (tStat->envstat==3) {
+ tc = patchCache->pitchsustain;
+ if (poly->sustain)
+ pitchSustain = true;
+ else
+ startDecay(EnvelopeType_pitch, tc);
+ } else {
+ if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
+ tStat->envstat++;
+
+ tStat->envbase = patchCache->pitchEnv.level[tStat->envstat];
+
+ Bit32u envTime = patchCache->pitchEnv.time[tStat->envstat];
+ int envDiff = abs(patchCache->pitchEnv.level[tStat->envstat] - patchCache->pitchEnv.level[tStat->envstat + 1]);
+ if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
+ envTime = synth->tables.envDeltaMaxTime[envDiff];
+ }
+
+ tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;
+
+ tStat->envpos = 0;
+ tStat->envsize++;
+ tStat->envdist = patchCache->pitchEnv.level[tStat->envstat + 1] - tStat->envbase;
+ }
+ tc = tStat->envbase;
+ tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ }
+ tStat->prevlevel = tc;
+ }
+ return tc;
+}
+
+void Partial::startDecayAll() {
+ startDecay(EnvelopeType_amp, envs[EnvelopeType_amp].prevlevel);
+ startDecay(EnvelopeType_filt, envs[EnvelopeType_filt].prevlevel);
+ startDecay(EnvelopeType_pitch, envs[EnvelopeType_pitch].prevlevel);
+ pitchSustain = false;
+}
+
+void Partial::startDecay(EnvelopeType envnum, Bit32s startval) {
+ EnvelopeStatus *tStat = &envs[envnum];
+
+ tStat->sustaining = false;
+ tStat->decaying = true;
+ tStat->envpos = 0;
+ tStat->envbase = startval;
+
+ switch (envnum) {
+ case EnvelopeType_amp:
+ tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->ampEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
+ tStat->envdist = -startval;
+ break;
+ case EnvelopeType_filt:
+ tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->filtEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf], 8);
+ tStat->envdist = -startval;
+ break;
+ case EnvelopeType_pitch:
+ tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->pitchEnv.time[3]], keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow], 8);
+ tStat->envdist = patchCache->pitchEnv.level[4] - startval;
+ break;
+ default:
+ break;
+ }
+ tStat->envsize++;
+}
diff --git a/engines/sci/sfx/softseq/mt32/partial.h b/engines/sci/sfx/softseq/mt32/partial.h
new file mode 100644
index 0000000000..93d8bcd985
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/partial.h
@@ -0,0 +1,148 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_PARTIAL_H
+#define MT32EMU_PARTIAL_H
+
+namespace MT32Emu {
+
+class Synth;
+struct NoteLookup;
+
+enum EnvelopeType {
+ EnvelopeType_amp = 0,
+ EnvelopeType_filt = 1,
+ EnvelopeType_pitch = 2
+};
+
+struct EnvelopeStatus {
+ Bit32s envpos;
+ Bit32s envstat;
+ Bit32s envbase;
+ Bit32s envdist;
+ Bit32s envsize;
+
+ bool sustaining;
+ bool decaying;
+ Bit32s prevlevel;
+
+ Bit32s counter;
+ Bit32s count;
+};
+
+// Class definition of MT-32 partials. 32 in all.
+class Partial {
+private:
+ Synth *synth;
+
+ int ownerPart; // -1 if unassigned
+ int mixType;
+ int structurePosition; // 0 or 1 of a structure pair
+ bool useNoisePair;
+
+ Bit16s myBuffer[MAX_SAMPLE_OUTPUT];
+
+ // Keyfollowed note value
+#if MT32EMU_ACCURATENOTES == 1
+ NoteLookup noteLookupStorage;
+ float noteVal;
+#else
+ int noteVal;
+ int fineShift;
+#endif
+ const NoteLookup *noteLookup; // LUTs for this noteVal
+ const KeyLookup *keyLookup; // LUTs for the clamped (12..108) key
+
+ // Keyfollowed filter values
+ int realVal;
+ int filtVal;
+
+ // Only used for PCM partials
+ int pcmNum;
+ PCMWaveEntry *pcmWave;
+
+ int pulsewidth;
+
+ Bit32u lfoPos;
+ soundaddr partialOff;
+
+ Bit32u ampEnvVal;
+ Bit32u pitchEnvVal;
+
+ float history[32];
+
+ bool pitchSustain;
+
+ int loopPos;
+
+ dpoly *poly;
+
+ int bendShift;
+
+ Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len);
+ Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len);
+ Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len);
+ void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len);
+
+ Bit32s getFiltEnvelope();
+ Bit32u getAmpEnvelope();
+ Bit32s getPitchEnvelope();
+
+ void initKeyFollow(int freqNum);
+
+public:
+ const PatchCache *patchCache;
+ EnvelopeStatus envs[3];
+ bool play;
+
+ PatchCache cachebackup;
+
+ Partial *pair;
+ bool alreadyOutputed;
+ Bit32u age;
+
+ Partial(Synth *synth);
+ ~Partial();
+
+ int getOwnerPart() const;
+ int getKey() const;
+ const dpoly *getDpoly() const;
+ bool isActive();
+ void activate(int part);
+ void deactivate(void);
+ void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial);
+ void startDecay(EnvelopeType envnum, Bit32s startval);
+ void startDecayAll();
+ void setBend(float factor);
+ bool shouldReverb();
+
+ // Returns true only if data written to buffer
+ // This function (unlike the one below it) returns processed stereo samples
+ // made from combining this single partial with its pair, if it has one.
+ bool produceOutput(Bit16s * partialBuf, long length);
+
+ // This function produces mono sample output using the partial's private internal buffer
+ Bit16s *generateSamples(long length);
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/partialManager.cpp b/engines/sci/sfx/softseq/mt32/partialManager.cpp
new file mode 100644
index 0000000000..3d3b6302db
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/partialManager.cpp
@@ -0,0 +1,272 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <string.h>
+
+#include "mt32emu.h"
+
+using namespace MT32Emu;
+
+PartialManager::PartialManager(Synth *useSynth) {
+ this->synth = useSynth;
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
+ partialTable[i] = new Partial(synth);
+}
+
+PartialManager::~PartialManager(void) {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
+ delete partialTable[i];
+}
+
+void PartialManager::getPerPartPartialUsage(int usage[9]) {
+ memset(usage, 0, 9 * sizeof (int));
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->isActive())
+ usage[partialTable[i]->getOwnerPart()]++;
+ }
+}
+
+void PartialManager::clearAlreadyOutputed() {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
+ partialTable[i]->alreadyOutputed = false;
+}
+
+void PartialManager::ageAll() {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
+ partialTable[i]->age++;
+}
+
+bool PartialManager::shouldReverb(int i) {
+ return partialTable[i]->shouldReverb();
+}
+
+bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) {
+ return partialTable[i]->produceOutput(buffer, bufferLength);
+}
+
+void PartialManager::deactivateAll() {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ partialTable[i]->deactivate();
+ }
+}
+
+unsigned int PartialManager::setReserve(Bit8u *rset) {
+ unsigned int pr = 0;
+ for (int x = 0; x < 9; x++) {
+ for (int y = 0; y < rset[x]; y++) {
+ partialReserveTable[pr] = x;
+ pr++;
+ }
+ }
+ return pr;
+}
+
+Partial *PartialManager::allocPartial(int partNum) {
+ Partial *outPartial = NULL;
+
+ // Use the first inactive partial reserved for the specified part (if there are any)
+ // Otherwise, use the last inactive partial, if any
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (!partialTable[i]->isActive()) {
+ outPartial = partialTable[i];
+ if (partialReserveTable[i] == partNum)
+ break;
+ }
+ }
+ if (outPartial != NULL) {
+ outPartial->activate(partNum);
+ outPartial->age = 0;
+ }
+ return outPartial;
+}
+
+unsigned int PartialManager::getFreePartialCount(void) {
+ int count = 0;
+ memset(partialPart, 0, sizeof(partialPart));
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (!partialTable[i]->isActive())
+ count++;
+ else
+ partialPart[partialTable[i]->getOwnerPart()]++;
+ }
+ return count;
+}
+
+/*
+bool PartialManager::freePartials(unsigned int needed, int partNum) {
+ int i;
+ int myPartPrior = (int)mt32ram.system.reserveSettings[partNum];
+ if (myPartPrior<partialPart[partNum]) {
+ //This can have more parts, must kill off those with less priority
+ int most, mostPart;
+ while (needed > 0) {
+ int selectPart = -1;
+ //Find the worst offender with more partials than allocated and kill them
+ most = -1;
+ mostPart = -1;
+ int diff;
+
+ for (i=0;i<9;i++) {
+ diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i];
+
+ if (diff>0) {
+ if (diff>most) {
+ most = diff;
+ mostPart = i;
+ }
+ }
+ }
+ selectPart = mostPart;
+ if (selectPart == -1) {
+ // All parts are within the allocated limits, you suck
+ // Look for first partial not of this part that's decaying perhaps?
+ return false;
+ }
+ bool found;
+ int oldest;
+ int oldnum;
+ while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) {
+ oldest = -1;
+ oldnum = -1;
+ found = false;
+ for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->isActive) {
+ if (partialTable[i]->ownerPart == selectPart) {
+ found = true;
+ if (partialTable[i]->age > oldest) {
+ oldest = partialTable[i]->age;
+ oldnum = i;
+ }
+ }
+ }
+ }
+ if (!found) break;
+ partialTable[oldnum]->deactivate();
+ --partialPart[selectPart];
+ --needed;
+ }
+
+ }
+ return true;
+
+ } else {
+ //This part has reached its max, must kill off its own
+ bool found;
+ int oldest;
+ int oldnum;
+ while (needed > 0) {
+ oldest = -1;
+ oldnum = -1;
+ found = false;
+ for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->isActive) {
+ if (partialTable[i]->ownerPart == partNum) {
+ found = true;
+ if (partialTable[i]->age > oldest) {
+ oldest = partialTable[i]->age;
+ oldnum = i;
+ }
+ }
+ }
+ }
+ if (!found) break;
+ partialTable[oldnum]->deactivate();
+ --needed;
+ }
+ // Couldn't free enough partials, sorry
+ if (needed>0) return false;
+ return true;
+ }
+
+}
+*/
+bool PartialManager::freePartials(unsigned int needed, int partNum) {
+ if (needed == 0) {
+ return true;
+ }
+ // Reclaim partials reserved for this part
+ // Kill those that are already decaying first
+ /*
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialReserveTable[i] == partNum) {
+ if (partialTable[i]->ownerPart != partNum) {
+ if (partialTable[i]->partCache->envs[AMPENV].decaying) {
+ partialTable[i]->isActive = false;
+ --needed;
+ if (needed == 0)
+ return true;
+ }
+ }
+ }
+ }*/
+ // Then kill those with the lowest part priority -- oldest at the moment
+ while (needed > 0) {
+ Bit32u prior = 0;
+ int priornum = -1;
+
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
+ /*
+ if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
+ prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart];
+ priornum = i;
+ }*/
+ if (partialTable[i]->age >= prior) {
+ prior = partialTable[i]->age;
+ priornum = i;
+ }
+ }
+ }
+ if (priornum != -1) {
+ partialTable[priornum]->deactivate();
+ --needed;
+ } else {
+ break;
+ }
+ }
+
+ // Kill off the oldest partials within this part
+ while (needed > 0) {
+ Bit32u oldest = 0;
+ int oldlist = -1;
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) {
+ if (partialTable[i]->age >= oldest) {
+ oldest = partialTable[i]->age;
+ oldlist = i;
+ }
+ }
+ }
+ if (oldlist != -1) {
+ partialTable[oldlist]->deactivate();
+ --needed;
+ } else {
+ break;
+ }
+ }
+ return needed == 0;
+}
+
+const Partial *PartialManager::getPartial(unsigned int partialNum) const {
+ if (partialNum > MT32EMU_MAX_PARTIALS - 1)
+ return NULL;
+ return partialTable[partialNum];
+}
diff --git a/engines/sci/sfx/softseq/mt32/partialManager.h b/engines/sci/sfx/softseq/mt32/partialManager.h
new file mode 100644
index 0000000000..b10f93ff02
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/partialManager.h
@@ -0,0 +1,56 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_PARTIALMANAGER_H
+#define MT32EMU_PARTIALMANAGER_H
+
+namespace MT32Emu {
+
+class Synth;
+
+class PartialManager {
+private:
+ Synth *synth; // Only used for sending debug output
+
+ Partial *partialTable[MT32EMU_MAX_PARTIALS];
+ Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS];
+ Bit32s partialPart[9]; // The count of partials played per part
+
+public:
+
+ PartialManager(Synth *synth);
+ ~PartialManager();
+ Partial *allocPartial(int partNum);
+ unsigned int getFreePartialCount(void);
+ bool freePartials(unsigned int needed, int partNum);
+ unsigned int setReserve(Bit8u *rset);
+ void deactivateAll();
+ void ageAll();
+ bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength);
+ bool shouldReverb(int i);
+ void clearAlreadyOutputed();
+ void getPerPartPartialUsage(int usage[9]);
+ const Partial *getPartial(unsigned int partialNum) const;
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/structures.h b/engines/sci/sfx/softseq/mt32/structures.h
new file mode 100644
index 0000000000..ef58c1d20f
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/structures.h
@@ -0,0 +1,284 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_STRUCTURES_H
+#define MT32EMU_STRUCTURES_H
+
+namespace MT32Emu {
+
+const unsigned int MAX_SAMPLE_OUTPUT = 4096;
+
+// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it
+// Roland provides documentation using the sysex-padded addresses, so we tend to use that in code and output
+#define MT32EMU_MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f))
+#define MT32EMU_SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f))
+
+#ifdef _MSC_VER
+#define MT32EMU_ALIGN_PACKED __declspec(align(1))
+typedef unsigned __int64 Bit64u;
+typedef signed __int64 Bit64s;
+#else
+#define MT32EMU_ALIGN_PACKED __attribute__((packed))
+typedef unsigned long long Bit64u;
+typedef signed long long Bit64s;
+#endif
+
+typedef unsigned int Bit32u;
+typedef signed int Bit32s;
+typedef unsigned short int Bit16u;
+typedef signed short int Bit16s;
+typedef unsigned char Bit8u;
+typedef signed char Bit8s;
+
+// The following structures represent the MT-32's memory
+// Since sysex allows this memory to be written to in blocks of bytes,
+// we keep this packed so that we can copy data into the various
+// banks directly
+#if defined(_MSC_VER) || defined (__MINGW32__)
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+struct TimbreParam {
+ struct commonParam {
+ char name[10];
+ Bit8u pstruct12; // 1&2 0-12 (1-13)
+ Bit8u pstruct34; // #3&4 0-12 (1-13)
+ Bit8u pmute; // 0-15 (0000-1111)
+ Bit8u nosustain; // 0-1(Normal, No sustain)
+ } MT32EMU_ALIGN_PACKED common;
+
+ struct partialParam {
+ struct wgParam {
+ Bit8u coarse; // 0-96 (C1,C#1-C9)
+ Bit8u fine; // 0-100 (-50 to +50 (cents?))
+ Bit8u keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
+ Bit8u bender; // 0,1 (ON/OFF)
+ Bit8u waveform; // MT-32: 0-1 (SQU/SAW); LAPC-I: WG WAVEFORM/PCM BANK 0 - 3 (SQU/1, SAW/1, SQU/2, SAW/2)
+ Bit8u pcmwave; // 0-127 (1-128)
+ Bit8u pulsewid; // 0-100
+ Bit8u pwvelo; // 0-14 (-7 - +7)
+ } MT32EMU_ALIGN_PACKED wg;
+
+ struct envParam {
+ Bit8u depth; // 0-10
+ Bit8u sensitivity; // 1-100
+ Bit8u timekeyfollow; // 0-4
+ Bit8u time[4]; // 1-100
+ Bit8u level[5]; // 1-100 (-50 - +50)
+ } MT32EMU_ALIGN_PACKED env;
+
+ struct lfoParam {
+ Bit8u rate; // 0-100
+ Bit8u depth; // 0-100
+ Bit8u modsense; // 0-100
+ } MT32EMU_ALIGN_PACKED lfo;
+
+ struct tvfParam {
+ Bit8u cutoff; // 0-100
+ Bit8u resonance; // 0-30
+ Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
+ Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel; // 0-14 (-7 - +7)
+ Bit8u envdepth; // 0-100
+ Bit8u envsense; // 0-100
+ Bit8u envdkf; // DEPTH KEY FOLL0W 0-4
+ Bit8u envtkf; // TIME KEY FOLLOW 0-4
+ Bit8u envtime[5]; // 1-100
+ Bit8u envlevel[4]; // 1-100
+ } MT32EMU_ALIGN_PACKED tvf;
+
+ struct tvaParam {
+ Bit8u level; // 0-100
+ Bit8u velosens; // 0-100
+ Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel1; // 0-12 (-12 - 0)
+ Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel2; // 0-12 (-12 - 0)
+ Bit8u envtkf; // TIME KEY FOLLOW 0-4
+ Bit8u envvkf; // VELOS KEY FOLL0W 0-4
+ Bit8u envtime[5]; // 1-100
+ Bit8u envlevel[4]; // 1-100
+ } MT32EMU_ALIGN_PACKED tva;
+ } MT32EMU_ALIGN_PACKED partial[4];
+} MT32EMU_ALIGN_PACKED;
+
+struct PatchParam {
+ Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
+ Bit8u timbreNum; // TIMBRE NUMBER 0-63
+ Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones)
+ Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents)
+ Bit8u benderRange; // BENDER RANGE 0-24
+ Bit8u assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
+ Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ Bit8u dummy; // (DUMMY)
+} MT32EMU_ALIGN_PACKED;
+
+struct MemParams {
+ // NOTE: The MT-32 documentation only specifies PatchTemp areas for parts 1-8.
+ // The LAPC-I documentation specified an additional area for rhythm at the end,
+ // where all parameters but fine tune, assign mode and output level are ignored
+ struct PatchTemp {
+ PatchParam patch;
+ Bit8u outlevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u dummyv[6];
+ } MT32EMU_ALIGN_PACKED;
+
+ PatchTemp patchSettings[9];
+
+ struct RhythmTemp {
+ Bit8u timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF)
+ Bit8u outlevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ } MT32EMU_ALIGN_PACKED;
+
+ RhythmTemp rhythmSettings[85];
+
+ TimbreParam timbreSettings[8];
+
+ PatchParam patches[128];
+
+ // NOTE: There are only 30 timbres in the "rhythm" bank for MT-32; the additional 34 are for LAPC-I and above
+ struct PaddedTimbre {
+ TimbreParam timbre;
+ Bit8u padding[10];
+ } MT32EMU_ALIGN_PACKED;
+
+ PaddedTimbre timbres[64 + 64 + 64 + 64]; // Group A, Group B, Memory, Rhythm
+
+ struct SystemArea {
+ Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
+ Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
+ Bit8u reverbTime; // REVERB TIME 0-7 (1-8)
+ Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8)
+ Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
+ Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
+ Bit8u masterVol; // MASTER VOLUME 0-100
+ } MT32EMU_ALIGN_PACKED;
+
+ SystemArea system;
+};
+
+#if defined(_MSC_VER) || defined (__MINGW32__)
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+struct PCMWaveEntry {
+ Bit32u addr;
+ Bit32u len;
+ double tune;
+ bool loop;
+};
+
+struct soundaddr {
+ Bit16u pcmplace;
+ Bit16u pcmoffset;
+};
+
+struct StereoVolume {
+ Bit16s leftvol;
+ Bit16s rightvol;
+};
+
+// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings
+struct PatchCache {
+ bool playPartial;
+ bool PCMPartial;
+ int pcm;
+ char waveform;
+ int pulsewidth;
+ int pwsens;
+
+ float pitch;
+
+ int lfodepth;
+ int lforate;
+ Bit32u lfoperiod;
+ int modsense;
+
+ float pitchKeyfollow;
+
+ int filtkeyfollow;
+
+ int tvfbias;
+ int tvfblevel;
+ int tvfdir;
+
+ int ampbias[2];
+ int ampblevel[2];
+ int ampdir[2];
+
+ int ampdepth;
+ int amplevel;
+
+ bool useBender;
+ float benderRange; // 0.0, 1.0, .., 24.0 (semitones)
+
+ TimbreParam::partialParam::envParam pitchEnv;
+ TimbreParam::partialParam::tvaParam ampEnv;
+ TimbreParam::partialParam::tvfParam filtEnv;
+
+ Bit32s pitchsustain;
+ Bit32s filtsustain;
+
+ Bit32u structureMix;
+ int structurePosition;
+ int structurePair;
+
+ // The following fields are actually common to all partials in the timbre
+ bool dirty;
+ Bit32u partialCount;
+ bool sustain;
+ float pitchShift;
+ bool reverb;
+ const StereoVolume *pansetptr;
+};
+
+class Partial; // Forward reference for class defined in partial.h
+
+struct dpoly {
+ bool isPlaying;
+
+ unsigned int key;
+ int freqnum;
+ int vel;
+
+ bool isDecay;
+
+ const Bit32u *volumeptr;
+
+ Partial *partials[4];
+
+ bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal
+ bool sustain;
+
+ bool isActive() const;
+ Bit32u getAge() const;
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/synth.cpp b/engines/sci/sfx/softseq/mt32/synth.cpp
new file mode 100644
index 0000000000..5ae196dc63
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/synth.cpp
@@ -0,0 +1,1199 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "mt32emu.h"
+
+#ifdef MACOSX
+// Older versions of Mac OS X didn't supply a powf function. To ensure
+// binary compatibility, we force using pow instead of powf (the only
+// potential drawback is that it might be a little bit slower).
+#define powf pow
+#endif
+
+namespace MT32Emu {
+
+const int MAX_SYSEX_SIZE = 512;
+
+const ControlROMMap ControlROMMaps[5] = {
+ // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrB tmbrBO, tmbrR trC rhythm rhyC rsrv panpot prog
+ {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57D0, 0x57E2}, // MT-32 revision 0
+ {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57E2, 0x57F4}, // MT-32 revision 0
+ {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57BA, 0x57CC}, // MT-32 revision 1
+ {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x741C, 85, 0x57E5, 0x57EE, 0x5800}, // MT-32 Blue Ridge mod
+ {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, 0x8080, 0x8000, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4F9C, 0x4FAE} // CM-32L
+ // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
+};
+
+float iir_filter_normal(float input, float *hist1_ptr, float *coef_ptr) {
+ float *hist2_ptr;
+ float output,new_hist;
+
+ hist2_ptr = hist1_ptr + 1; // next history
+
+ // 1st number of coefficients array is overall input scale factor, or filter gain
+ output = input * (*coef_ptr++);
+
+ output = output - *hist1_ptr * (*coef_ptr++);
+ new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
+
+ output = new_hist + *hist1_ptr * (*coef_ptr++);
+ output = output + *hist2_ptr * (*coef_ptr++); // zeros
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+ hist1_ptr++;
+ hist2_ptr++;
+
+ // i = 1
+ output = output - *hist1_ptr * (*coef_ptr++);
+ new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
+
+ output = new_hist + *hist1_ptr * (*coef_ptr++);
+ output = output + *hist2_ptr * (*coef_ptr++); // zeros
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+
+ return(output);
+}
+
+Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
+ for (unsigned int i = 0; i < len; i++) {
+ checksum = checksum + data[i];
+ }
+ checksum = checksum & 0x7f;
+ if (checksum)
+ checksum = 0x80 - checksum;
+ return checksum;
+}
+
+Synth::Synth() {
+ isOpen = false;
+ reverbModel = NULL;
+ partialManager = NULL;
+ memset(parts, 0, sizeof(parts));
+}
+
+Synth::~Synth() {
+ close(); // Make sure we're closed and everything is freed
+}
+
+int Synth::report(ReportType type, const void *data) {
+ if (myProp.report != NULL) {
+ return myProp.report(myProp.userData, type, data);
+ }
+ return 0;
+}
+
+void Synth::printDebug(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ if (myProp.printDebug != NULL) {
+ myProp.printDebug(myProp.userData, fmt, ap);
+ } else {
+ vprintf(fmt, ap);
+ printf("\n");
+ }
+ va_end(ap);
+}
+
+void Synth::initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel) {
+ // FIXME:KG: I don't think it's necessary to recreate the reverbModel... Just set the parameters
+ if (reverbModel != NULL)
+ delete reverbModel;
+ reverbModel = new revmodel();
+
+ switch (newRevMode) {
+ case 0:
+ reverbModel->setroomsize(.1f);
+ reverbModel->setdamp(.75f);
+ break;
+ case 1:
+ reverbModel->setroomsize(.5f);
+ reverbModel->setdamp(.5f);
+ break;
+ case 2:
+ reverbModel->setroomsize(.5f);
+ reverbModel->setdamp(.1f);
+ break;
+ case 3:
+ reverbModel->setroomsize(1.0f);
+ reverbModel->setdamp(.75f);
+ break;
+ default:
+ reverbModel->setroomsize(.1f);
+ reverbModel->setdamp(.5f);
+ break;
+ }
+ reverbModel->setdry(1);
+ reverbModel->setwet((float)newRevLevel / 8.0f);
+ reverbModel->setwidth((float)newRevTime / 8.0f);
+}
+
+File *Synth::openFile(const char *filename, File::OpenMode mode) {
+ if (myProp.openFile != NULL) {
+ return myProp.openFile(myProp.userData, filename, mode);
+ }
+ char pathBuf[2048];
+ if (myProp.baseDir != NULL) {
+ strcpy(&pathBuf[0], myProp.baseDir);
+ strcat(&pathBuf[0], filename);
+ filename = pathBuf;
+ }
+ ANSIFile *file = new ANSIFile();
+ if (!file->open(filename, mode)) {
+ delete file;
+ return NULL;
+ }
+ return file;
+}
+
+void Synth::closeFile(File *file) {
+ if (myProp.closeFile != NULL) {
+ myProp.closeFile(myProp.userData, file);
+ } else {
+ file->close();
+ delete file;
+ }
+}
+
+bool Synth::loadPreset(File *file) {
+ bool inSys = false;
+ Bit8u sysexBuf[MAX_SYSEX_SIZE];
+ Bit16u syslen = 0;
+ bool rc = true;
+ for (;;) {
+ Bit8u c;
+ if (!file->readBit8u(&c)) {
+ if (!file->isEOF()) {
+ rc = false;
+ }
+ break;
+ }
+ sysexBuf[syslen] = c;
+ if (inSys) {
+ syslen++;
+ if (c == 0xF7) {
+ playSysex(&sysexBuf[0], syslen);
+ inSys = false;
+ syslen = 0;
+ } else if (syslen == MAX_SYSEX_SIZE) {
+ printDebug("MAX_SYSEX_SIZE (%d) exceeded while processing preset, ignoring message", MAX_SYSEX_SIZE);
+ inSys = false;
+ syslen = 0;
+ }
+ } else if (c == 0xF0) {
+ syslen++;
+ inSys = true;
+ }
+ }
+ return rc;
+}
+
+bool Synth::loadControlROM(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read); // ROM File
+ if (file == NULL) {
+ return false;
+ }
+ bool rc = (file->read(controlROMData, CONTROL_ROM_SIZE) == CONTROL_ROM_SIZE);
+
+ closeFile(file);
+ if (!rc)
+ return rc;
+
+ // Control ROM successfully loaded, now check whether it's a known type
+ controlROMMap = NULL;
+ for (unsigned int i = 0; i < sizeof (ControlROMMaps) / sizeof (ControlROMMaps[0]); i++) {
+ if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) {
+ controlROMMap = &ControlROMMaps[i];
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Synth::loadPCMROM(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read); // ROM File
+ if (file == NULL) {
+ return false;
+ }
+ bool rc = true;
+ int i;
+ for (i = 0; i < pcmROMSize; i++) {
+ Bit8u s;
+ if (!file->readBit8u(&s)) {
+ if (!file->isEOF()) {
+ rc = false;
+ }
+ break;
+ }
+ Bit8u c;
+ if (!file->readBit8u(&c)) {
+ if (!file->isEOF()) {
+ rc = false;
+ } else {
+ printDebug("PCM ROM file has an odd number of bytes! Ignoring last");
+ }
+ break;
+ }
+
+ short e;
+ int bit;
+ int u;
+ int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
+
+ e = 0;
+ for (u = 0; u < 15; u++) {
+ if (order[u] < 8)
+ bit = (s >> (7 - order[u])) & 0x1;
+ else
+ bit = (c >> (7 - (order[u] - 8))) & 0x1;
+ e = e | (short)(bit << (15 - u));
+ }
+
+ /*
+ //Bit16s e = ( ((s & 0x7f) << 4) | ((c & 0x40) << 6) | ((s & 0x80) << 6) | ((c & 0x3f))) << 2;
+ if (e<0)
+ e = -32767 - e;
+ int ut = abs(e);
+ int dif = 0x7fff - ut;
+ x = exp(((float)((float)0x8000-(float)dif) / (float)0x1000));
+ e = (int)((float)e * (x/3200));
+ */
+
+ // File is companded (dB?), convert to linear PCM
+ // MINDB = -96
+ // MAXDB = -15
+ float testval;
+ testval = (float)((~e) & 0x7fff);
+ testval = -(testval / 400.00f);
+ //testval = -(testval / 341.32291666666666666666666666667);
+ float vol = powf(8, testval / 20) * 32767.0f;
+
+ if (e > 0)
+ vol = -vol;
+
+ pcmROMData[i] = (Bit16s)vol;
+ }
+ if (i != pcmROMSize) {
+ printDebug("PCM ROM file is too short (expected %d, got %d)", pcmROMSize, i);
+ rc = false;
+ }
+ closeFile(file);
+ return rc;
+}
+
+bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
+ ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
+ for (int i = 0; i < count; i++) {
+ int rAddr = tps[i].pos * 0x800;
+ int rLenExp = (tps[i].len & 0x70) >> 4;
+ int rLen = 0x800 << rLenExp;
+ bool rLoop = (tps[i].len & 0x80) != 0;
+ //Bit8u rFlag = tps[i].len & 0x0F;
+ Bit16u rTuneOffset = (tps[i].pitchMSB << 8) | tps[i].pitchLSB;
+ // The number below is confirmed to a reasonable degree of accuracy on CM-32L
+ double STANDARDFREQ = 442.0;
+ float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4056.0 - 9.0 / 12.0));
+ //printDebug("%f,%d,%d", pTune, tps[i].pitchCoarse, tps[i].pitchFine);
+ if (rAddr + rLen > pcmROMSize) {
+ printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen);
+ return false;
+ }
+ pcmWaves[i].addr = rAddr;
+ pcmWaves[i].len = rLen;
+ pcmWaves[i].loop = rLoop;
+ pcmWaves[i].tune = rTune;
+ }
+ return false;
+}
+
+bool Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem, unsigned int memLen) {
+ if (memLen < sizeof(TimbreParam::commonParam)) {
+ return false;
+ }
+ TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
+ memcpy(&timbre->common, mem, 14);
+ unsigned int memPos = 14;
+ char drumname[11];
+ strncpy(drumname, timbre->common.name, 10);
+ drumname[10] = 0;
+ for (int t = 0; t < 4; t++) {
+ if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
+ if (memPos + 58 >= memLen) {
+ return false;
+ }
+ memcpy(&timbre->partial[t], mem + memPos, 58);
+ memPos += 58;
+ }
+ }
+ return true;
+}
+
+bool Synth::initRhythmTimbres(Bit16u mapAddress, Bit16u count) {
+ const Bit8u *drumMap = &controlROMData[mapAddress];
+ int timbreNum = 192;
+ for (Bit16u i = 0; i < count * 2; i += 2) {
+ Bit16u address = (drumMap[i + 1] << 8) | drumMap[i];
+ /*
+ // This check is nonsensical when the control ROM is the full 64KB addressable by 16-bit absolute pointers (which it is)
+ if (address >= CONTROL_ROM_SIZE) {
+ printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
+ return false;
+ }
+ */
+ if (!initRhythmTimbre(timbreNum++, &controlROMData[address], CONTROL_ROM_SIZE - address)) {
+ printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre 0x%04x", i, address);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre) {
+ for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) {
+ Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i];
+ if (address + sizeof(TimbreParam) > CONTROL_ROM_SIZE) {
+ printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
+ return false;
+ }
+ address = address + offset;
+ TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre;
+ memcpy(timbre, &controlROMData[address], sizeof(TimbreParam));
+ }
+ return true;
+}
+
+bool Synth::open(SynthProperties &useProp) {
+ if (isOpen)
+ return false;
+
+ myProp = useProp;
+ if (useProp.baseDir != NULL) {
+ myProp.baseDir = new char[strlen(useProp.baseDir) + 1];
+ strcpy(myProp.baseDir, useProp.baseDir);
+ }
+
+ // This is to help detect bugs
+ memset(&mt32ram, '?', sizeof(mt32ram));
+
+ printDebug("Loading Control ROM");
+ if (!loadControlROM("CM32L_CONTROL.ROM")) {
+ if (!loadControlROM("MT32_CONTROL.ROM")) {
+ printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM");
+ report(ReportType_errorControlROM, NULL);
+ return false;
+ }
+ }
+
+ // 512KB PCM ROM for MT-32, etc.
+ // 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500
+ // Note that the size below is given in samples (16-bit), not bytes
+ pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024;
+ pcmROMData = new Bit16s[pcmROMSize];
+
+ printDebug("Loading PCM ROM");
+ if (!loadPCMROM("CM32L_PCM.ROM")) {
+ if (!loadPCMROM("MT32_PCM.ROM")) {
+ printDebug("Init Error - Missing MT32_PCM.ROM");
+ report(ReportType_errorPCMROM, NULL);
+ return false;
+ }
+ }
+
+ printDebug("Initialising Timbre Bank A");
+ if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0)) {
+ return false;
+ }
+
+ printDebug("Initialising Timbre Bank B");
+ if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 64)) {
+ return false;
+ }
+
+ printDebug("Initialising Timbre Bank R");
+ if (!initRhythmTimbres(controlROMMap->timbreRMap, controlROMMap->timbreRCount)) {
+ return false;
+ }
+
+ printDebug("Initialising Timbre Bank M");
+ // CM-64 seems to initialise all bytes in this bank to 0.
+ memset(&mt32ram.timbres[128], 0, sizeof (mt32ram.timbres[128]) * 64);
+
+ partialManager = new PartialManager(this);
+
+ pcmWaves = new PCMWaveEntry[controlROMMap->pcmCount];
+
+ printDebug("Initialising PCM List");
+ initPCMList(controlROMMap->pcmTable, controlROMMap->pcmCount);
+
+ printDebug("Initialising Rhythm Temp");
+ memcpy(mt32ram.rhythmSettings, &controlROMData[controlROMMap->rhythmSettings], controlROMMap->rhythmSettingsCount * 4);
+
+ printDebug("Initialising Patches");
+ for (Bit8u i = 0; i < 128; i++) {
+ PatchParam *patch = &mt32ram.patches[i];
+ patch->timbreGroup = i / 64;
+ patch->timbreNum = i % 64;
+ patch->keyShift = 24;
+ patch->fineTune = 50;
+ patch->benderRange = 12;
+ patch->assignMode = 0;
+ patch->reverbSwitch = 1;
+ patch->dummy = 0;
+ }
+
+ printDebug("Initialising System");
+ // The MT-32 manual claims that "Standard pitch" is 442Hz.
+ mt32ram.system.masterTune = 0x4A; // Confirmed on CM-64
+ mt32ram.system.reverbMode = 0; // Confirmed
+ mt32ram.system.reverbTime = 5; // Confirmed
+ mt32ram.system.reverbLevel = 3; // Confirmed
+ memcpy(mt32ram.system.reserveSettings, &controlROMData[controlROMMap->reserveSettings], 9); // Confirmed
+ for (Bit8u i = 0; i < 9; i++) {
+ // This is the default: {1, 2, 3, 4, 5, 6, 7, 8, 9}
+ // An alternative configuration can be selected by holding "Master Volume"
+ // and pressing "PART button 1" on the real MT-32's frontpanel.
+ // The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9}
+ mt32ram.system.chanAssign[i] = i + 1;
+ }
+ mt32ram.system.masterVol = 100; // Confirmed
+ if (!refreshSystem())
+ return false;
+
+ for (int i = 0; i < 8; i++) {
+ mt32ram.patchSettings[i].outlevel = 80;
+ mt32ram.patchSettings[i].panpot = controlROMData[controlROMMap->panSettings + i];
+ memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv));
+ parts[i] = new Part(this, i);
+ parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
+ }
+ parts[8] = new RhythmPart(this, 8);
+
+ // For resetting mt32 mid-execution
+ mt32default = mt32ram;
+
+ iirFilter = &iir_filter_normal;
+
+#ifdef MT32EMU_HAVE_X86
+ bool availableSSE = DetectSIMD();
+ bool available3DNow = Detect3DNow();
+
+ if (availableSSE)
+ report(ReportType_availableSSE, NULL);
+ if (available3DNow)
+ report(ReportType_available3DNow, NULL);
+
+ if (available3DNow) {
+ printDebug("Detected and using SIMD (AMD 3DNow) extensions");
+ iirFilter = &iir_filter_3dnow;
+ report(ReportType_using3DNow, NULL);
+ } else if (availableSSE) {
+ printDebug("Detected and using SIMD (Intel SSE) extensions");
+ iirFilter = &iir_filter_sse;
+ report(ReportType_usingSSE, NULL);
+ }
+#endif
+
+ isOpen = true;
+ isEnabled = false;
+
+ printDebug("*** Initialisation complete ***");
+ return true;
+}
+
+void Synth::close(void) {
+ if (!isOpen)
+ return;
+
+ tables.freeNotes();
+ if (partialManager != NULL) {
+ delete partialManager;
+ partialManager = NULL;
+ }
+
+ if (reverbModel != NULL) {
+ delete reverbModel;
+ reverbModel = NULL;
+ }
+
+ for (int i = 0; i < 9; i++) {
+ if (parts[i] != NULL) {
+ delete parts[i];
+ parts[i] = NULL;
+ }
+ }
+ if (myProp.baseDir != NULL) {
+ delete myProp.baseDir;
+ myProp.baseDir = NULL;
+ }
+
+ delete[] pcmWaves;
+ delete[] pcmROMData;
+ isOpen = false;
+}
+
+void Synth::playMsg(Bit32u msg) {
+ // FIXME: Implement active sensing
+ unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
+ unsigned char chan = (unsigned char) (msg & 0x00000F);
+ unsigned char note = (unsigned char)((msg & 0x00FF00) >> 8);
+ unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16);
+ isEnabled = true;
+
+ //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
+
+ char part = chantable[chan];
+ if (part < 0 || part > 8) {
+ printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity);
+ return;
+ }
+ playMsgOnPart(part, code, note, velocity);
+}
+
+void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
+ Bit32u bend;
+
+ //printDebug("Synth::playMsg(0x%02x)",msg);
+ switch (code) {
+ case 0x8:
+ //printDebug("Note OFF - Part %d", part);
+ // The MT-32 ignores velocity for note off
+ parts[part]->stopNote(note);
+ break;
+ case 0x9:
+ //printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity);
+ if (velocity == 0) {
+ // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40
+ parts[part]->stopNote(note);
+ } else {
+ parts[part]->playNote(note, velocity);
+ }
+ break;
+ case 0xB: // Control change
+ switch (note) {
+ case 0x01: // Modulation
+ //printDebug("Modulation: %d", velocity);
+ parts[part]->setModulation(velocity);
+ break;
+ case 0x07: // Set volume
+ //printDebug("Volume set: %d", velocity);
+ parts[part]->setVolume(velocity);
+ break;
+ case 0x0A: // Pan
+ //printDebug("Pan set: %d", velocity);
+ parts[part]->setPan(velocity);
+ break;
+ case 0x0B:
+ //printDebug("Expression set: %d", velocity);
+ parts[part]->setExpression(velocity);
+ break;
+ case 0x40: // Hold (sustain) pedal
+ //printDebug("Hold pedal set: %d", velocity);
+ parts[part]->setHoldPedal(velocity>=64);
+ break;
+
+ case 0x79: // Reset all controllers
+ //printDebug("Reset all controllers");
+ //FIXME: Check for accuracy against real thing
+ parts[part]->setVolume(100);
+ parts[part]->setExpression(127);
+ parts[part]->setPan(64);
+ parts[part]->setBend(0x2000);
+ parts[part]->setHoldPedal(false);
+ break;
+
+ case 0x7B: // All notes off
+ //printDebug("All notes off");
+ parts[part]->allNotesOff();
+ break;
+
+ default:
+ printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity);
+ break;
+ }
+
+ break;
+ case 0xC: // Program change
+ //printDebug("Program change %01x", note);
+ parts[part]->setProgram(note);
+ break;
+ case 0xE: // Pitch bender
+ bend = (velocity << 7) | (note);
+ //printDebug("Pitch bender %02x", bend);
+ parts[part]->setBend(bend);
+ break;
+ default:
+ printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
+ break;
+ }
+
+ //midiOutShortMsg(m_out, msg);
+}
+
+void Synth::playSysex(const Bit8u *sysex, Bit32u len) {
+ if (len < 2) {
+ printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
+ }
+ if (sysex[0] != 0xF0) {
+ printDebug("playSysex: Message lacks start-of-sysex (0xF0)");
+ return;
+ }
+ // Due to some programs (e.g. Java) sending buffers with junk at the end, we have to go through and find the end marker rather than relying on len.
+ Bit32u endPos;
+ for (endPos = 1; endPos < len; endPos++)
+ {
+ if (sysex[endPos] == 0xF7)
+ break;
+ }
+ if (endPos == len) {
+ printDebug("playSysex: Message lacks end-of-sysex (0xf7)");
+ return;
+ }
+ playSysexWithoutFraming(sysex + 1, endPos - 1);
+}
+
+void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) {
+ if (len < 4) {
+ printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
+ return;
+ }
+ if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) {
+ printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ if (sysex[2] == SYSEX_MDL_D50) {
+ printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ else if (sysex[2] != SYSEX_MDL_MT32) {
+ printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4);
+}
+
+void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) {
+ if (device > 0x10) {
+ // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels
+ printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device);
+ return;
+ }
+ if (len < 4) {
+ printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
+ return;
+ }
+ unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
+ if (checksum != sysex[len - 1]) {
+ printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
+ return;
+ }
+ len -= 1; // Exclude checksum
+ switch (command) {
+ case SYSEX_CMD_DT1:
+ writeSysex(device, sysex, len);
+ break;
+ case SYSEX_CMD_RQ1:
+ readSysex(device, sysex, len);
+ break;
+ default:
+ printDebug("playSysexWithoutFraming: Unsupported command %02x", command);
+ return;
+ }
+}
+
+void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) {
+}
+
+const MemoryRegion memoryRegions[8] = {
+ {MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9},
+ {MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85},
+ {MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8},
+ {MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128},
+ {MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64},
+ {MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::SystemArea), 1},
+ {MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1},
+ {MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1}
+};
+
+const int NUM_REGIONS = sizeof(memoryRegions) / sizeof(MemoryRegion);
+
+void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
+ Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
+ addr = MT32EMU_MEMADDR(addr);
+ sysex += 3;
+ len -= 3;
+ //printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr));
+ // NOTE: Please keep both lower and upper bounds in each check, for ease of reading
+
+ // Process channel-specific sysex by converting it to device-global
+ if (device < 0x10) {
+ printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
+ if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
+ int offset;
+ if (chantable[device] == -1) {
+ printDebug(" (Channel not mapped to a partial... 0 offset)");
+ offset = 0;
+ } else if (chantable[device] == 8) {
+ printDebug(" (Channel mapped to rhythm... 0 offset)");
+ offset = 0;
+ } else {
+ offset = chantable[device] * sizeof(MemParams::PatchTemp);
+ printDebug(" (Setting extra offset to %d)", offset);
+ }
+ addr += MT32EMU_MEMADDR(0x030000) + offset;
+ } else if (/*addr >= 0x010000 && */ addr < MT32EMU_MEMADDR(0x020000)) {
+ addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
+ } else if (/*addr >= 0x020000 && */ addr < MT32EMU_MEMADDR(0x030000)) {
+ int offset;
+ if (chantable[device] == -1) {
+ printDebug(" (Channel not mapped to a partial... 0 offset)");
+ offset = 0;
+ } else if (chantable[device] == 8) {
+ printDebug(" (Channel mapped to rhythm... 0 offset)");
+ offset = 0;
+ } else {
+ offset = chantable[device] * sizeof(TimbreParam);
+ printDebug(" (Setting extra offset to %d)", offset);
+ }
+ addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset;
+ } else {
+ printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
+ return;
+ }
+ }
+
+ // Process device-global sysex (possibly converted from channel-specific sysex above)
+ for (;;) {
+ // Find the appropriate memory region
+ int regionNum;
+ const MemoryRegion *region = NULL; // Initialised to please compiler
+ for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
+ region = &memoryRegions[regionNum];
+ if (region->contains(addr)) {
+ writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex);
+ break;
+ }
+ }
+ if (regionNum == NUM_REGIONS) {
+ printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len);
+ break;
+ }
+ Bit32u next = region->next(addr, len);
+ if (next == 0) {
+ break;
+ }
+ addr += next;
+ sysex += next;
+ len -= next;
+ }
+}
+
+void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) {
+ int regionNum;
+ const MemoryRegion *region = NULL;
+ for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
+ region = &memoryRegions[regionNum];
+ if (region->contains(addr)) {
+ readMemoryRegion(region, addr, len, data);
+ break;
+ }
+ }
+}
+
+void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data) {
+ unsigned int first = region->firstTouched(addr);
+ //unsigned int last = region->lastTouched(addr, len);
+ unsigned int off = region->firstTouchedOffset(addr);
+ len = region->getClampedLen(addr, len);
+
+ unsigned int m;
+
+ switch(region->type) {
+ case MR_PatchTemp:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.patchSettings[first])[off + m];
+ break;
+ case MR_RhythmTemp:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m];
+ break;
+ case MR_TimbreTemp:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.timbreSettings[first])[off + m];
+ break;
+ case MR_Patches:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.patches[first])[off + m];
+ break;
+ case MR_Timbres:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.timbres[first])[off + m];
+ break;
+ case MR_System:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.system)[m + off];
+ break;
+ default:
+ for (m = 0; m < len; m += 2) {
+ data[m] = 0xff;
+ if (m + 1 < len) {
+ data[m+1] = (Bit8u)region->type;
+ }
+ }
+ // TODO: Don't care about the others ATM
+ break;
+ }
+
+}
+
+void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data) {
+ unsigned int first = region->firstTouched(addr);
+ unsigned int last = region->lastTouched(addr, len);
+ unsigned int off = region->firstTouchedOffset(addr);
+ switch (region->type) {
+ case MR_PatchTemp:
+ for (unsigned int m = 0; m < len; m++) {
+ ((Bit8u *)&mt32ram.patchSettings[first])[off + m] = data[m];
+ }
+ //printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);
+
+ for (unsigned int i = first; i <= last; i++) {
+ int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum;
+ char timbreName[11];
+ memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10);
+ timbreName[10] = 0;
+ printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s), outlevel=%d", first, last, off, off + len, i, absTimbreNum, timbreName, mt32ram.patchSettings[i].outlevel);
+ if (parts[i] != NULL) {
+ if (i != 8) {
+ // Note: Confirmed on CM-64 that we definitely *should* update the timbre here,
+ // but only in the case that the sysex actually writes to those values
+ if (i == first && off > 2) {
+ printDebug(" (Not updating timbre, since those values weren't touched)");
+ } else {
+ parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
+ }
+ }
+ parts[i]->refresh();
+ }
+ }
+ break;
+ case MR_RhythmTemp:
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m] = data[m];
+ for (unsigned int i = first; i <= last; i++) {
+ int timbreNum = mt32ram.rhythmSettings[i].timbre;
+ char timbreName[11];
+ if (timbreNum < 94) {
+ memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10);
+ timbreName[10] = 0;
+ } else {
+ strcpy(timbreName, "[None]");
+ }
+ printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", first, last, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName);
+ }
+ if (parts[8] != NULL) {
+ parts[8]->refresh();
+ }
+ break;
+ case MR_TimbreTemp:
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.timbreSettings[first])[off + m] = data[m];
+ for (unsigned int i = first; i <= last; i++) {
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.timbreSettings[i].common.name, 10);
+ instrumentName[10] = 0;
+ printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", first, last, off, off + len, i, instrumentName);
+ if (parts[i] != NULL) {
+ parts[i]->refresh();
+ }
+ }
+ break;
+ case MR_Patches:
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.patches[first])[off + m] = data[m];
+ for (unsigned int i = first; i <= last; i++) {
+ PatchParam *patch = &mt32ram.patches[i];
+ int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10);
+ instrumentName[10] = 0;
+ Bit8u *n = (Bit8u *)patch;
+ printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", first, last, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]);
+ // FIXME:KG: The below is definitely dodgy. We just guess that this is the patch that the part was using
+ // based on a timbre match (but many patches could have the same timbre!)
+ // If this refresh is really correct, we should store the patch number in use by each part.
+ /*
+ for (int part = 0; part < 8; part++) {
+ if (parts[part] != NULL) {
+ int partPatchAbsTimbreNum = mt32ram.patchSettings[part].patch.timbreGroup * 64 + mt32ram.patchSettings[part].patch.timbreNum;
+ if (parts[part]->getAbsTimbreNum() == patchAbsTimbreNum) {
+ parts[part]->setPatch(patch);
+ parts[part]->RefreshPatch();
+ }
+ }
+ }
+ */
+ }
+ break;
+ case MR_Timbres:
+ // Timbres
+ first += 128;
+ last += 128;
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.timbres[first])[off + m] = data[m];
+ for (unsigned int i = first; i <= last; i++) {
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.timbres[i].timbre.common.name, 10);
+ instrumentName[10] = 0;
+ printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", first, last, off, off + len, i, instrumentName);
+ // FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)...
+ // Does the real MT-32 automatically do this?
+ for (unsigned int part = 0; part < 9; part++) {
+ if (parts[part] != NULL) {
+ parts[part]->refreshTimbre(i);
+ }
+ }
+ }
+ break;
+ case MR_System:
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.system)[m + off] = data[m];
+
+ report(ReportType_devReconfig, NULL);
+
+ printDebug("WRITE-SYSTEM:");
+ refreshSystem();
+ break;
+ case MR_Display:
+ char buf[MAX_SYSEX_SIZE];
+ memcpy(&buf, &data[0], len);
+ buf[len] = 0;
+ printDebug("WRITE-LCD: %s", buf);
+ report(ReportType_lcdMessage, buf);
+ break;
+ case MR_Reset:
+ printDebug("RESET");
+ report(ReportType_devReset, NULL);
+ partialManager->deactivateAll();
+ mt32ram = mt32default;
+ for (int i = 0; i < 9; i++) {
+ parts[i]->refresh();
+ }
+ isEnabled = false;
+ break;
+ }
+}
+
+bool Synth::refreshSystem() {
+ memset(chantable, -1, sizeof(chantable));
+
+ for (unsigned int i = 0; i < 9; i++) {
+ //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.system.chanAssign[i]);
+ if (mt32ram.system.chanAssign[i] == 16 && parts[i] != NULL) {
+ parts[i]->allSoundOff();
+ } else {
+ chantable[(int)mt32ram.system.chanAssign[i]] = (char)i;
+ }
+ }
+ //FIXME:KG: This is just an educated guess.
+ // The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here)
+ // The MT-32 documentation claims a range of 432.1Hz-457.6Hz
+ masterTune = 440.0f * powf(2.0f, (mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f));
+ printDebug(" Master Tune: %f", masterTune);
+ printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
+ report(ReportType_newReverbMode, &mt32ram.system.reverbMode);
+ report(ReportType_newReverbTime, &mt32ram.system.reverbTime);
+ report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel);
+
+ if (myProp.useDefaultReverb) {
+ initReverb(mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
+ } else {
+ initReverb(myProp.reverbType, myProp.reverbTime, mt32ram.system.reverbLevel);
+ }
+
+ Bit8u *rset = mt32ram.system.reserveSettings;
+ printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
+ int pr = partialManager->setReserve(rset);
+ if (pr != 32)
+ printDebug(" (Partial Reserve Table with less than 32 partials reserved!)");
+ rset = mt32ram.system.chanAssign;
+ printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
+ printDebug(" Master volume: %d", mt32ram.system.masterVol);
+ masterVolume = (Bit16u)(mt32ram.system.masterVol * 32767 / 100);
+ if (!tables.init(this, pcmWaves, (float)myProp.sampleRate, masterTune)) {
+ report(ReportType_errorSampleRate, NULL);
+ return false;
+ }
+ return true;
+}
+
+bool Synth::dumpTimbre(File *file, const TimbreParam *timbre, Bit32u address) {
+ // Sysex header
+ if (!file->writeBit8u(0xF0))
+ return false;
+ if (!file->writeBit8u(0x41))
+ return false;
+ if (!file->writeBit8u(0x10))
+ return false;
+ if (!file->writeBit8u(0x16))
+ return false;
+ if (!file->writeBit8u(0x12))
+ return false;
+
+ char lsb = (char)(address & 0x7f);
+ char isb = (char)((address >> 7) & 0x7f);
+ char msb = (char)(((address >> 14) & 0x7f) | 0x08);
+
+ //Address
+ if (!file->writeBit8u(msb))
+ return false;
+ if (!file->writeBit8u(isb))
+ return false;
+ if (!file->writeBit8u(lsb))
+ return false;
+
+ //Data
+ if (file->write(timbre, 246) != 246)
+ return false;
+
+ //Checksum
+ unsigned char checksum = calcSysexChecksum((const Bit8u *)timbre, 246, msb + isb + lsb);
+ if (!file->writeBit8u(checksum))
+ return false;
+
+ //End of sysex
+ if (!file->writeBit8u(0xF7))
+ return false;
+ return true;
+}
+
+int Synth::dumpTimbres(const char *filename, int start, int len) {
+ File *file = openFile(filename, File::OpenMode_write);
+ if (file == NULL)
+ return -1;
+
+ for (int timbreNum = start; timbreNum < start + len; timbreNum++) {
+ int useaddr = (timbreNum - start) * 256;
+ TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
+ if (!dumpTimbre(file, timbre, useaddr))
+ break;
+ }
+ closeFile(file);
+ return 0;
+}
+
+void ProduceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
+#if MT32EMU_USE_MMX > 2
+ //FIXME:KG: This appears to introduce crackle
+ int donelen = i386_produceOutput1(useBuf, stream, len, volume);
+ len -= donelen;
+ stream += donelen * 2;
+ useBuf += donelen * 2;
+#endif
+ int end = len * 2;
+ while (end--) {
+ *stream = *stream + (Bit16s)(((Bit32s)*useBuf++ * (Bit32s)volume)>>15);
+ stream++;
+ }
+}
+
+void Synth::render(Bit16s *stream, Bit32u len) {
+ memset(stream, 0, len * sizeof (Bit16s) * 2);
+ if (!isEnabled)
+ return;
+ while (len > 0) {
+ Bit32u thisLen = len > MAX_SAMPLE_OUTPUT ? MAX_SAMPLE_OUTPUT : len;
+ doRender(stream, thisLen);
+ len -= thisLen;
+ stream += 2 * thisLen;
+ }
+}
+
+void Synth::doRender(Bit16s *stream, Bit32u len) {
+ partialManager->ageAll();
+
+ if (myProp.useReverb) {
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialManager->shouldReverb(i)) {
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
+ }
+ }
+ }
+ Bit32u m = 0;
+ for (unsigned int i = 0; i < len; i++) {
+ sndbufl[i] = (float)stream[m] / 32767.0f;
+ m++;
+ sndbufr[i] = (float)stream[m] / 32767.0f;
+ m++;
+ }
+ reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
+ m=0;
+ for (unsigned int i = 0; i < len; i++) {
+ stream[m] = (Bit16s)(outbufl[i] * 32767.0f);
+ m++;
+ stream[m] = (Bit16s)(outbufr[i] * 32767.0f);
+ m++;
+ }
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (!partialManager->shouldReverb(i)) {
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
+ }
+ }
+ }
+ } else {
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len))
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
+ }
+ }
+
+ partialManager->clearAlreadyOutputed();
+
+#if MT32EMU_MONITOR_PARTIALS == 1
+ samplepos += len;
+ if (samplepos > myProp.SampleRate * 5) {
+ samplepos = 0;
+ int partialUsage[9];
+ partialManager->GetPerPartPartialUsage(partialUsage);
+ printDebug("1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7]);
+ printDebug("Rhythm: %02d TOTAL: %02d", partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->GetFreePartialCount());
+ }
+#endif
+}
+
+const Partial *Synth::getPartial(unsigned int partialNum) const {
+ return partialManager->getPartial(partialNum);
+}
+
+const Part *Synth::getPart(unsigned int partNum) const {
+ if (partNum > 8)
+ return NULL;
+ return parts[partNum];
+}
+
+}
diff --git a/engines/sci/sfx/softseq/mt32/synth.h b/engines/sci/sfx/softseq/mt32/synth.h
new file mode 100644
index 0000000000..9d57c8d3cd
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/synth.h
@@ -0,0 +1,300 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_SYNTH_H
+#define MT32EMU_SYNTH_H
+
+#include <stdarg.h>
+
+class revmodel;
+
+namespace MT32Emu {
+
+class File;
+class TableInitialiser;
+class Partial;
+class PartialManager;
+class Part;
+
+enum ReportType {
+ // Errors
+ ReportType_errorControlROM = 1,
+ ReportType_errorPCMROM,
+ ReportType_errorSampleRate,
+
+ // Progress
+ ReportType_progressInit,
+
+ // HW spec
+ ReportType_availableSSE,
+ ReportType_available3DNow,
+ ReportType_usingSSE,
+ ReportType_using3DNow,
+
+ // General info
+ ReportType_lcdMessage,
+ ReportType_devReset,
+ ReportType_devReconfig,
+ ReportType_newReverbMode,
+ ReportType_newReverbTime,
+ ReportType_newReverbLevel
+};
+
+struct SynthProperties {
+ // Sample rate to use in mixing
+ int sampleRate;
+
+ // Flag to activate reverb. True = use reverb, False = no reverb
+ bool useReverb;
+ // True to use software set reverb settings, False to set reverb settings in
+ // following parameters
+ bool useDefaultReverb;
+ // When not using the default settings, this specifies one of the 4 reverb types
+ // 1 = Room 2 = Hall 3 = Plate 4 = Tap
+ unsigned char reverbType;
+ // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
+ unsigned char reverbTime;
+ // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
+ unsigned char reverbLevel;
+ // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash)
+ // Not used if "openFile" is set. May be NULL in any case.
+ char *baseDir;
+ // This is used as the first argument to all callbacks
+ void *userData;
+ // Callback for reporting various errors and information. May be NULL
+ int (*report)(void *userData, ReportType type, const void *reportData);
+ // Callback for debug messages, in vprintf() format
+ void (*printDebug)(void *userData, const char *fmt, va_list list);
+ // Callback for providing an implementation of File, opened and ready for use
+ // May be NULL, in which case a default implementation will be used.
+ File *(*openFile)(void *userData, const char *filename, File::OpenMode mode);
+ // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted.
+ void (*closeFile)(void *userData, File *file);
+};
+
+// This is the specification of the Callback routine used when calling the RecalcWaveforms
+// function
+typedef void (*recalcStatusCallback)(int percDone);
+
+// This external function recreates the base waveform file (waveforms.raw) using a specifed
+// sampling rate. The callback routine provides interactivity to let the user know what
+// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the
+// callback routine, no status is reported.
+bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
+
+typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr);
+
+const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
+
+const Bit8u SYSEX_MDL_MT32 = 0x16;
+const Bit8u SYSEX_MDL_D50 = 0x14;
+
+const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1
+const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1
+const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data
+const Bit8u SYSEX_CMD_RQD = 0x41; // Request data
+const Bit8u SYSEX_CMD_DAT = 0x42; // Data set
+const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge
+const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
+const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
+const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
+
+const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
+
+struct ControlROMPCMStruct
+{
+ Bit8u pos;
+ Bit8u len;
+ Bit8u pitchLSB;
+ Bit8u pitchMSB;
+};
+
+struct ControlROMMap {
+ Bit16u idPos;
+ Bit16u idLen;
+ const char *idBytes;
+ Bit16u pcmTable;
+ Bit16u pcmCount;
+ Bit16u timbreAMap;
+ Bit16u timbreAOffset;
+ Bit16u timbreBMap;
+ Bit16u timbreBOffset;
+ Bit16u timbreRMap;
+ Bit16u timbreRCount;
+ Bit16u rhythmSettings;
+ Bit16u rhythmSettingsCount;
+ Bit16u reserveSettings;
+ Bit16u panSettings;
+ Bit16u programSettings;
+};
+
+enum MemoryRegionType {
+ MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
+};
+
+class MemoryRegion {
+public:
+ MemoryRegionType type;
+ Bit32u startAddr, entrySize, entries;
+
+ int lastTouched(Bit32u addr, Bit32u len) const {
+ return (offset(addr) + len - 1) / entrySize;
+ }
+ int firstTouchedOffset(Bit32u addr) const {
+ return offset(addr) % entrySize;
+ }
+ int firstTouched(Bit32u addr) const {
+ return offset(addr) / entrySize;
+ }
+ Bit32u regionEnd() const {
+ return startAddr + entrySize * entries;
+ }
+ bool contains(Bit32u addr) const {
+ return addr >= startAddr && addr < regionEnd();
+ }
+ int offset(Bit32u addr) const {
+ return addr - startAddr;
+ }
+ Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
+ if (addr + len > regionEnd())
+ return regionEnd() - addr;
+ return len;
+ }
+ Bit32u next(Bit32u addr, Bit32u len) const {
+ if (addr + len > regionEnd()) {
+ return regionEnd() - addr;
+ }
+ return 0;
+ }
+};
+
+
+class Synth {
+friend class Part;
+friend class RhythmPart;
+friend class Partial;
+friend class Tables;
+private:
+ bool isEnabled;
+
+ iir_filter_type iirFilter;
+
+ PCMWaveEntry *pcmWaves; // Array
+
+ const ControlROMMap *controlROMMap;
+ Bit8u controlROMData[CONTROL_ROM_SIZE];
+ Bit16s *pcmROMData;
+ int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
+
+ Bit8s chantable[32];
+
+ #if MT32EMU_MONITOR_PARTIALS == 1
+ static Bit32s samplepos = 0;
+ #endif
+
+ Tables tables;
+
+ MemParams mt32ram, mt32default;
+
+ revmodel *reverbModel;
+
+ float masterTune;
+ Bit16u masterVolume;
+
+ bool isOpen;
+
+ PartialManager *partialManager;
+ Part *parts[9];
+
+ Bit16s tmpBuffer[MAX_SAMPLE_OUTPUT * 2];
+ float sndbufl[MAX_SAMPLE_OUTPUT];
+ float sndbufr[MAX_SAMPLE_OUTPUT];
+ float outbufl[MAX_SAMPLE_OUTPUT];
+ float outbufr[MAX_SAMPLE_OUTPUT];
+
+ SynthProperties myProp;
+
+ bool loadPreset(File *file);
+ void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel);
+ void doRender(Bit16s * stream, Bit32u len);
+
+ void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+ void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+ void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data);
+ void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data);
+
+ bool loadControlROM(const char *filename);
+ bool loadPCMROM(const char *filename);
+ bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr);
+ int dumpTimbres(const char *filename, int start, int len);
+
+ bool initPCMList(Bit16u mapAddress, Bit16u count);
+ bool initRhythmTimbres(Bit16u mapAddress, Bit16u count);
+ bool initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre);
+ bool initRhythmTimbre(int drumNum, const Bit8u *mem, unsigned int memLen);
+ bool refreshSystem();
+
+protected:
+ int report(ReportType type, const void *reportData);
+ File *openFile(const char *filename, File::OpenMode mode);
+ void closeFile(File *file);
+ void printDebug(const char *fmt, ...);
+
+public:
+ static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
+
+ Synth();
+ ~Synth();
+
+ // Used to initialise the MT-32. Must be called before any other function.
+ // Returns true if initialization was sucessful, otherwise returns false.
+ bool open(SynthProperties &useProp);
+
+ // Closes the MT-32 and deallocates any memory used by the synthesizer
+ void close(void);
+
+ // Sends a 4-byte MIDI message to the MT-32 for immediate playback
+ void playMsg(Bit32u msg);
+ void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
+
+ // Sends a string of Sysex commands to the MT-32 for immediate interpretation
+ // The length is in bytes
+ void playSysex(const Bit8u *sysex, Bit32u len);
+ void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
+ void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len);
+ void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+
+ // This callback routine is used to have the MT-32 generate samples to the specified
+ // output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo,
+ // one sample is 4 bytes)
+ void render(Bit16s * stream, Bit32u len);
+
+ const Partial *getPartial(unsigned int partialNum) const;
+
+ void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
+
+ // partNum should be 0..7 for Part 1..8, or 8 for Rhythm
+ const Part *getPart(unsigned int partNum) const;
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/tables.cpp b/engines/sci/sfx/softseq/mt32/tables.cpp
new file mode 100644
index 0000000000..4591ea22e3
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/tables.cpp
@@ -0,0 +1,749 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "mt32emu.h"
+
+#ifdef MACOSX
+// Older versions of Mac OS X didn't supply a powf function. To ensure
+// binary compatibility, we force using pow instead of powf (the only
+// potential drawback is that it might be a little bit slower).
+#define powf pow
+#endif
+
+#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))
+
+namespace MT32Emu {
+
+//Amplitude time velocity follow exponential coefficients
+static const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
+static const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};
+
+// These are division constants for the TVF depth key follow
+static const Bit32u depexp[5] = {3000, 950, 485, 255, 138};
+
+//Envelope time keyfollow exponential coefficients
+static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215};
+static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067};
+
+// Begin filter stuff
+
+// Pre-warp the coefficients of a numerator or denominator.
+// Note that a0 is assumed to be 1, so there is no wrapping
+// of it.
+static void prewarp(double *a1, double *a2, double fc, double fs) {
+ double wp;
+
+ wp = 2.0 * fs * tan(DOUBLE_PI * fc / fs);
+
+ *a2 = *a2 / (wp * wp);
+ *a1 = *a1 / wp;
+}
+
+// Transform the numerator and denominator coefficients
+// of s-domain biquad section into corresponding
+// z-domain coefficients.
+//
+// Store the 4 IIR coefficients in array pointed by coef
+// in following order:
+// beta1, beta2 (denominator)
+// alpha1, alpha2 (numerator)
+//
+// Arguments:
+// a0-a2 - s-domain numerator coefficients
+// b0-b2 - s-domain denominator coefficients
+// k - filter gain factor. initially set to 1
+// and modified by each biquad section in such
+// a way, as to make it the coefficient by
+// which to multiply the overall filter gain
+// in order to achieve a desired overall filter gain,
+// specified in initial value of k.
+// fs - sampling rate (Hz)
+// coef - array of z-domain coefficients to be filled in.
+//
+// Return:
+// On return, set coef z-domain coefficients
+static void bilinear(double a0, double a1, double a2, double b0, double b1, double b2, double *k, double fs, float *coef) {
+ double ad, bd;
+
+ // alpha (Numerator in s-domain)
+ ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0;
+ // beta (Denominator in s-domain)
+ bd = 4. * b2 * fs * fs + 2. * b1* fs + b0;
+
+ // update gain constant for this section
+ *k *= ad/bd;
+
+ // Denominator
+ *coef++ = (float)((2. * b0 - 8. * b2 * fs * fs) / bd); // beta1
+ *coef++ = (float)((4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd); // beta2
+
+ // Nominator
+ *coef++ = (float)((2. * a0 - 8. * a2 * fs * fs) / ad); // alpha1
+ *coef = (float)((4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad); // alpha2
+}
+
+// a0-a2: numerator coefficients
+// b0-b2: denominator coefficients
+// fc: Filter cutoff frequency
+// fs: sampling rate
+// k: overall gain factor
+// coef: pointer to 4 iir coefficients
+static void szxform(double *a0, double *a1, double *a2, double *b0, double *b1, double *b2, double fc, double fs, double *k, float *coef) {
+ // Calculate a1 and a2 and overwrite the original values
+ prewarp(a1, a2, fc, fs);
+ prewarp(b1, b2, fc, fs);
+ bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef);
+}
+
+static void initFilter(float fs, float fc, float *icoeff, float Q) {
+ float *coef;
+ double a0, a1, a2, b0, b1, b2;
+
+ double k = 1.5; // Set overall filter gain factor
+ coef = icoeff + 1; // Skip k, or gain
+
+ // Section 1
+ a0 = 1.0;
+ a1 = 0;
+ a2 = 0;
+ b0 = 1.0;
+ b1 = 0.765367 / Q; // Divide by resonance or Q
+ b2 = 1.0;
+ szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
+ coef += 4; // Point to next filter section
+
+ // Section 2
+ a0 = 1.0;
+ a1 = 0;
+ a2 = 0;
+ b0 = 1.0;
+ b1 = 1.847759 / Q;
+ b2 = 1.0;
+ szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
+
+ icoeff[0] = (float)k;
+}
+
+void Tables::initFiltCoeff(float samplerate) {
+ for (int j = 0; j < FILTERGRAN; j++) {
+ for (int res = 0; res < 31; res++) {
+ float tres = resonanceFactor[res];
+ initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtCoeff[j][res], tres);
+ }
+ }
+}
+
+void Tables::initEnvelopes(float samplerate) {
+ for (int lf = 0; lf <= 100; lf++) {
+ float elf = (float)lf;
+
+ // General envelope
+ // This formula fits observation of the CM-32L by +/- 0.03s or so for the second time value in the filter,
+ // when all other times were 0 and all levels were 100. Note that variations occur depending on the level
+ // delta of the section, which we're not fully emulating.
+ float seconds = powf(2.0f, (elf / 8.0f) + 7.0f) / 32768.0f;
+ int samples = (int)(seconds * samplerate);
+ envTime[lf] = samples;
+
+ // Cap on envelope times depending on the level delta
+ if (elf == 0) {
+ envDeltaMaxTime[lf] = 63;
+ } else {
+ float cap = 11.0f * (float)log(elf) + 64;
+ if (cap > 100.0f) {
+ cap = 100.0f;
+ }
+ envDeltaMaxTime[lf] = (int)cap;
+ }
+
+
+ // This (approximately) represents the time durations when the target level is 0.
+ // Not sure why this is a special case, but it's seen to be from the real thing.
+ seconds = powf(2, (elf / 8.0f) + 6) / 32768.0f;
+ envDecayTime[lf] = (int)(seconds * samplerate);
+
+ // I am certain of this: Verified by hand LFO log
+ lfoPeriod[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f, (float)lf) * 0.021236044f));
+ }
+}
+
+void Tables::initMT32ConstantTables(Synth *synth) {
+ int lf;
+ synth->printDebug("Initialising Pitch Tables");
+ for (lf = -108; lf <= 108; lf++) {
+ tvfKeyfollowMult[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f)));
+ //synth->printDebug("KT %d = %d", f, keytable[f+108]);
+ }
+
+ for (int res = 0; res < 31; res++) {
+ resonanceFactor[res] = powf((float)res / 30.0f, 5.0f) + 1.0f;
+ }
+
+ int period = 65536;
+
+ for (int ang = 0; ang < period; ang++) {
+ int halfang = (period / 2);
+ int angval = ang % halfang;
+ float tval = (((float)angval / (float)halfang) - 0.5f) * 2;
+ if (ang >= halfang)
+ tval = -tval;
+ sintable[ang] = (Bit16s)(tval * 50.0f) + 50;
+ }
+
+ int velt, dep;
+ float tempdep;
+ for (velt = 0; velt < 128; velt++) {
+ for (dep = 0; dep < 5; dep++) {
+ if (dep > 0) {
+ float ff = (float)(exp(3.5f * tvcatconst[dep] * (59.0f - (float)velt)) * tvcatmult[dep]);
+ tempdep = 256.0f * ff;
+ envTimeVelfollowMult[dep][velt] = (int)tempdep;
+ //if ((velt % 16) == 0) {
+ // synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep);
+ //}
+ } else
+ envTimeVelfollowMult[dep][velt] = 256;
+ }
+
+ for (dep = -7; dep < 8; dep++) {
+ float fldep = (float)abs(dep) / 7.0f;
+ fldep = powf(fldep,2.5f);
+ if (dep < 0)
+ fldep = fldep * -1.0f;
+ pwVelfollowAdd[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0);
+ }
+ }
+
+ for (dep = 0; dep <= 100; dep++) {
+ for (velt = 0; velt < 128; velt++) {
+ float fdep = (float)dep * 0.000347013f; // Another MT-32 constant
+ float fv = ((float)velt - 64.0f)/7.26f;
+ float flogdep = powf(10, fdep * fv);
+ float fbase;
+
+ if (velt > 64)
+ synth->tables.tvfVelfollowMult[velt][dep] = (int)(flogdep * 256.0);
+ else {
+ //lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96));
+ fbase = 1 - (powf(((float)dep / 100.0f),.25f) * ((float)(64-velt) / 96.0f));
+ synth->tables.tvfVelfollowMult[velt][dep] = (int)(fbase * 256.0);
+ }
+ //synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]);
+ }
+ }
+
+ for (lf = 0; lf < 128; lf++) {
+ float veloFract = lf / 127.0f;
+ for (int velsens = 0; velsens <= 100; velsens++) {
+ float sensFract = (velsens - 50) / 50.0f;
+ if (velsens < 50) {
+ tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, veloFract * -sensFract * 127.0f / 20.0f), 8);
+ } else {
+ tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, (1.0f - veloFract) * sensFract * 127.0f / 20.0f), 8);
+ }
+ }
+ }
+
+ for (lf = 0; lf <= 100; lf++) {
+ // Converts the 0-100 range used by the MT-32 to volume multiplier
+ volumeMult[lf] = FIXEDPOINT_MAKE(powf((float)lf / 100.0f, FLOAT_LN), 7);
+ }
+
+ for (lf = 0; lf <= 100; lf++) {
+ float mv = lf / 100.0f;
+ float pt = mv - 0.5f;
+ if (pt < 0)
+ pt = 0;
+
+ // Original (CC version)
+ //pwFactor[lf] = (int)(pt * 210.04f) + 128;
+
+ // Approximation from sample comparison
+ pwFactor[lf] = (int)(pt * 179.0f) + 128;
+ }
+
+ for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {
+ int myRand;
+ myRand = rand();
+ //myRand = ((myRand - 16383) * 7168) >> 16;
+ // This one is slower but works with all values of RAND_MAX
+ myRand = (int)((myRand - RAND_MAX / 2) / (float)RAND_MAX * (7168 / 2));
+ //FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason
+ noiseBuf[i] = (Bit16s)myRand;
+ }
+
+ float tdist;
+ float padjtable[51];
+ for (lf = 0; lf <= 50; lf++) {
+ if (lf == 0)
+ padjtable[lf] = 7;
+ else if (lf == 1)
+ padjtable[lf] = 6;
+ else if (lf == 2)
+ padjtable[lf] = 5;
+ else if (lf == 3)
+ padjtable[lf] = 4;
+ else if (lf == 4)
+ padjtable[lf] = 4 - (0.333333f);
+ else if (lf == 5)
+ padjtable[lf] = 4 - (0.333333f * 2);
+ else if (lf == 6)
+ padjtable[lf] = 3;
+ else if ((lf > 6) && (lf <= 12)) {
+ tdist = (lf-6.0f) / 6.0f;
+ padjtable[lf] = 3.0f - tdist;
+ } else if ((lf > 12) && (lf <= 25)) {
+ tdist = (lf - 12.0f) / 13.0f;
+ padjtable[lf] = 2.0f - tdist;
+ } else {
+ tdist = (lf - 25.0f) / 25.0f;
+ padjtable[lf] = 1.0f - tdist;
+ }
+ //synth->printDebug("lf %d = padj %f", lf, padjtable[lf]);
+ }
+
+ float lfp, depf, finalval, tlf;
+ int depat, pval, depti;
+ for (lf = 0; lf <= 10; lf++) {
+ // I believe the depth is cubed or something
+
+ for (depat = 0; depat <= 100; depat++) {
+ if (lf > 0) {
+ depti = abs(depat - 50);
+ tlf = (float)lf - padjtable[depti];
+ if (tlf < 0)
+ tlf = 0;
+ lfp = (float)exp(0.713619942f * tlf) / 407.4945111f;
+
+ if (depat < 50)
+ finalval = 4096.0f * powf(2, -lfp);
+ else
+ finalval = 4096.0f * powf(2, lfp);
+ pval = (int)finalval;
+
+ pitchEnvVal[lf][depat] = pval;
+ //synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp);
+ } else {
+ pitchEnvVal[lf][depat] = 4096;
+ //synth->printDebug("lf %d depat %d pval 4096", lf, depat);
+ }
+ }
+ }
+ for (lf = 0; lf <= 100; lf++) {
+ // It's linear - verified on MT-32 - one of the few things linear
+ lfp = ((float)lf * 0.1904f) / 310.55f;
+
+ for (depat = 0; depat <= 100; depat++) {
+ depf = ((float)depat - 50.0f) / 50.0f;
+ //finalval = pow(2, lfp * depf * .5);
+ finalval = 4096.0f + (4096.0f * lfp * depf);
+
+ pval = (int)finalval;
+
+ lfoShift[lf][depat] = pval;
+
+ //synth->printDebug("lf %d depat %d pval %x", lf,depat,pval);
+ }
+ }
+
+ for (lf = 0; lf <= 12; lf++) {
+ for (int distval = 0; distval < 128; distval++) {
+ float amplog, dval;
+ if (lf == 0) {
+ amplog = 0;
+ dval = 1;
+ tvaBiasMult[lf][distval] = 256;
+ } else {
+ /*
+ amplog = powf(1.431817011f, (float)lf) / FLOAT_PI;
+ dval = ((128.0f - (float)distval) / 128.0f);
+ amplog = exp(amplog);
+ dval = powf(amplog, dval) / amplog;
+ tvaBiasMult[lf][distval] = (int)(dval * 256.0);
+ */
+ // Lets assume for a second it's linear
+
+ // Distance of full volume reduction
+ amplog = (float)(12.0f / (float)lf) * 24.0f;
+ if (distval > amplog) {
+ tvaBiasMult[lf][distval] = 0;
+ } else {
+ dval = (amplog - (float)distval) / amplog;
+ tvaBiasMult[lf][distval] = (int)(dval * 256.0f);
+ }
+ }
+ //synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvaBiasMult[lf][distval],amplog);
+ }
+ }
+
+ for (lf = 0; lf <= 14; lf++) {
+ for (int distval = 0; distval < 128; distval++) {
+ float filval = fabsf((float)((lf - 7) * 12) / 7.0f);
+ float amplog, dval;
+ if (lf == 7) {
+ amplog = 0;
+ dval = 1;
+ tvfBiasMult[lf][distval] = 256;
+ } else {
+ //amplog = pow(1.431817011, filval) / FLOAT_PI;
+ amplog = powf(1.531817011f, filval) / FLOAT_PI;
+ dval = (128.0f - (float)distval) / 128.0f;
+ amplog = (float)exp(amplog);
+ dval = powf(amplog,dval)/amplog;
+ if (lf < 8) {
+ tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
+ } else {
+ dval = powf(dval, 0.3333333f);
+ if (dval < 0.01f)
+ dval = 0.01f;
+ dval = 1 / dval;
+ tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
+ }
+ }
+ //synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvfBiasMult[lf][distval],amplog);
+ }
+ }
+}
+
+// Per-note table initialisation follows
+
+static void initSaw(NoteLookup *noteLookup, Bit32s div2) {
+ int tmpdiv = div2 << 16;
+ for (int rsaw = 0; rsaw <= 100; rsaw++) {
+ float fsaw;
+ if (rsaw < 50)
+ fsaw = 50.0f;
+ else
+ fsaw = (float)rsaw;
+
+ //(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132
+ float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f;
+ noteLookup->sawTable[rsaw] = (int)(sawfact * (float)tmpdiv) >> 16;
+ //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div, rsaw, sawtable[f][rsaw]);
+ }
+}
+
+static void initDep(KeyLookup *keyLookup, float f) {
+ for (int dep = 0; dep < 5; dep++) {
+ if (dep == 0) {
+ keyLookup->envDepthMult[dep] = 256;
+ keyLookup->envTimeMult[dep] = 256;
+ } else {
+ float depfac = 3000.0f;
+ float ff, tempdep;
+ depfac = (float)depexp[dep];
+
+ ff = (f - (float)MIDDLEC) / depfac;
+ tempdep = powf(2, ff) * 256.0f;
+ keyLookup->envDepthMult[dep] = (int)tempdep;
+
+ ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]);
+ keyLookup->envTimeMult[dep] = (int)(ff * 256.0f);
+ }
+ }
+ //synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]);
+}
+
+Bit16s Tables::clampWF(Synth *synth, const char *n, float ampVal, double input) {
+ Bit32s x = (Bit32s)(input * ampVal);
+ if (x < -ampVal - 1) {
+ synth->printDebug("%s==%d<-WGAMP-1!", n, x);
+ x = (Bit32s)(-ampVal - 1);
+ } else if (x > ampVal) {
+ synth->printDebug("%s==%d>WGAMP!", n, x);
+ x = (Bit32s)ampVal;
+ }
+ return (Bit16s)x;
+}
+
+File *Tables::initWave(Synth *synth, NoteLookup *noteLookup, float ampVal, float div2, File *file) {
+ int iDiv2 = (int)div2;
+ noteLookup->waveformSize[0] = iDiv2 << 1;
+ noteLookup->waveformSize[1] = iDiv2 << 1;
+ noteLookup->waveformSize[2] = iDiv2 << 2;
+ for (int i = 0; i < 3; i++) {
+ if (noteLookup->waveforms[i] == NULL) {
+ noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]];
+ }
+ }
+ if (file != NULL) {
+ for (int i = 0; i < 3 && file != NULL; i++) {
+ size_t len = noteLookup->waveformSize[i];
+ for (unsigned int j = 0; j < len; j++) {
+ if (!file->readBit16u((Bit16u *)&noteLookup->waveforms[i][j])) {
+ synth->printDebug("Error reading wave file cache!");
+ file->close();
+ file = NULL;
+ break;
+ }
+ }
+ }
+ }
+ if (file == NULL) {
+ double sd = DOUBLE_PI / div2;
+
+ for (int fa = 0; fa < (iDiv2 << 1); fa++) {
+ // sa ranges from 0 to 2PI
+ double sa = fa * sd;
+
+ // Calculate a sample for the bandlimited sawtooth wave
+ double saw = 0.0;
+ int sincs = iDiv2 >> 1;
+ double sinus = 1.0;
+ for (int sincNum = 1; sincNum <= sincs; sincNum++) {
+ saw += sin(sinus * sa) / sinus;
+ sinus++;
+ }
+
+ // This works pretty well
+ // Multiplied by 0.84 so that the spikes caused by bandlimiting don't overdrive the amplitude
+ noteLookup->waveforms[0][fa] = clampWF(synth, "saw", ampVal, -saw / (0.5 * DOUBLE_PI) * 0.84);
+ noteLookup->waveforms[1][fa] = clampWF(synth, "cos", ampVal, -cos(sa / 2.0));
+ noteLookup->waveforms[2][fa * 2] = clampWF(synth, "cosoff_0", ampVal, -cos(sa - DOUBLE_PI));
+ noteLookup->waveforms[2][fa * 2 + 1] = clampWF(synth, "cosoff_1", ampVal, -cos((sa + (sd / 2)) - DOUBLE_PI));
+ }
+ }
+ return file;
+}
+
+static void initFiltTable(NoteLookup *noteLookup, float freq, float rate) {
+ for (int tr = 0; tr <= 200; tr++) {
+ float ftr = (float)tr;
+
+ // Verified exact on MT-32
+ if (tr > 100)
+ ftr = 100.0f + (powf((ftr - 100.0f) / 100.0f, 3.0f) * 100.0f);
+
+ // I think this is the one
+ float brsq = powf(10.0f, (tr / 50.0f) - 1.0f);
+ noteLookup->filtTable[0][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN);
+ if (noteLookup->filtTable[0][tr]>=((FILTERGRAN*15)/16))
+ noteLookup->filtTable[0][tr] = ((FILTERGRAN*15)/16);
+
+ float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f;
+ noteLookup->filtTable[1][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN);
+ if (noteLookup->filtTable[1][tr]>=((FILTERGRAN*15)/16))
+ noteLookup->filtTable[1][tr] = ((FILTERGRAN*15)/16);
+ }
+}
+
+static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) {
+ for (int cf = 0; cf <= 100; cf++) {
+ float cfmult = (float)cf;
+
+ for (int tf = 0;tf <= 100; tf++) {
+ float tfadd = (float)tf;
+
+ //float freqsum = exp((cfmult + tfadd) / 30.0f) / 4.0f;
+ //float freqsum = 0.15f * exp(0.45f * ((cfmult + tfadd) / 10.0f));
+
+ float freqsum = powf(2.0f, ((cfmult + tfadd) - 40.0f) / 16.0f);
+
+ noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN);
+ if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16))
+ noteLookup->nfiltTable[cf][tf] = ((FILTERGRAN * 15) / 16);
+ }
+ }
+}
+
+File *Tables::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry *pcmWaves, File *file) {
+ float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));
+ float div2 = rate * 2.0f / freq;
+ noteLookup->div2 = (int)div2;
+
+ if (noteLookup->div2 == 0)
+ noteLookup->div2 = 1;
+
+ initSaw(noteLookup, noteLookup->div2);
+
+ //synth->printDebug("Note %f; freq=%f, div=%f", note, freq, rate / freq);
+ file = initWave(synth, noteLookup, (const float)WGAMP, div2, file);
+
+ // Create the pitch tables
+ if (noteLookup->wavTable == NULL)
+ noteLookup->wavTable = new Bit32u[synth->controlROMMap->pcmCount];
+ double rateMult = 32000.0 / rate;
+ double tuner = freq * 65536.0f;
+ for (int pc = 0; pc < synth->controlROMMap->pcmCount; pc++) {
+ noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);
+ }
+
+ initFiltTable(noteLookup, freq, rate);
+ initNFiltTable(noteLookup, freq, rate);
+ return file;
+}
+
+bool Tables::initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float masterTune) {
+ const char *NoteNames[12] = {
+ "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "
+ };
+ char filename[64];
+ int intRate = (int)rate;
+ char version[4] = {0, 0, 0, 5};
+ sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune);
+
+ File *file = NULL;
+ char header[20];
+ strncpy(header, "MT32WAVE", 8);
+ int pos = 8;
+ // Version...
+ for (int i = 0; i < 4; i++)
+ header[pos++] = version[i];
+ header[pos++] = (char)((intRate >> 24) & 0xFF);
+ header[pos++] = (char)((intRate >> 16) & 0xFF);
+ header[pos++] = (char)((intRate >> 8) & 0xFF);
+ header[pos++] = (char)(intRate & 0xFF);
+ int intTuning = (int)masterTune;
+ header[pos++] = (char)((intTuning >> 8) & 0xFF);
+ header[pos++] = (char)(intTuning & 0xFF);
+ header[pos++] = 0;
+ header[pos] = (char)((masterTune - intTuning) * 10);
+#if MT32EMU_WAVECACHEMODE < 2
+ bool reading = false;
+ file = synth->openFile(filename, File::OpenMode_read);
+ if (file != NULL) {
+ char fileHeader[20];
+ if (file->read(fileHeader, 20) == 20) {
+ if (memcmp(fileHeader, header, 20) == 0) {
+ Bit16u endianCheck;
+ if (file->readBit16u(&endianCheck)) {
+ if (endianCheck == 1) {
+ reading = true;
+ } else {
+ synth->printDebug("Endian check in %s does not match expected", filename);
+ }
+ } else {
+ synth->printDebug("Unable to read endian check in %s", filename);
+ }
+ } else {
+ synth->printDebug("Header of %s does not match expected", filename);
+ }
+ } else {
+ synth->printDebug("Error reading 16 bytes of %s", filename);
+ }
+ if (!reading) {
+ file->close();
+ file = NULL;
+ }
+ } else {
+ synth->printDebug("Unable to open %s for reading", filename);
+ }
+#endif
+
+ float progress = 0.0f;
+ bool abort = false;
+ synth->report(ReportType_progressInit, &progress);
+ for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) {
+ synth->printDebug("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 2);
+ NoteLookup *noteLookup = &noteLookups[f - LOWEST_NOTE];
+ file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file);
+ progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES;
+ abort = synth->report(ReportType_progressInit, &progress) != 0;
+ if (abort)
+ break;
+ }
+
+#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2
+ if (file == NULL) {
+ file = synth->openFile(filename, File::OpenMode_write);
+ if (file != NULL) {
+ if (file->write(header, 20) == 20 && file->writeBit16u(1)) {
+ for (int f = 0; f < NUM_NOTES; f++) {
+ for (int i = 0; i < 3 && file != NULL; i++) {
+ int len = noteLookups[f].waveformSize[i];
+ for (int j = 0; j < len; j++) {
+ if (!file->writeBit16u(noteLookups[f].waveforms[i][j])) {
+ synth->printDebug("Error writing waveform cache file");
+ file->close();
+ file = NULL;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ synth->printDebug("Error writing 16-byte header to %s - won't continue saving", filename);
+ }
+ } else {
+ synth->printDebug("Unable to open %s for writing - won't be created", filename);
+ }
+ }
+#endif
+
+ if (file != NULL)
+ synth->closeFile(file);
+ return !abort;
+}
+
+void Tables::freeNotes() {
+ for (int t = 0; t < 3; t++) {
+ for (int m = 0; m < NUM_NOTES; m++) {
+ if (noteLookups[m].waveforms[t] != NULL) {
+ delete[] noteLookups[m].waveforms[t];
+ noteLookups[m].waveforms[t] = NULL;
+ noteLookups[m].waveformSize[t] = 0;
+ }
+ if (noteLookups[m].wavTable != NULL) {
+ delete[] noteLookups[m].wavTable;
+ noteLookups[m].wavTable = NULL;
+ }
+ }
+ }
+ initialisedMasterTune = 0.0f;
+}
+
+Tables::Tables() {
+ initialisedSampleRate = 0.0f;
+ initialisedMasterTune = 0.0f;
+ memset(&noteLookups, 0, sizeof(noteLookups));
+}
+
+bool Tables::init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune) {
+ if (sampleRate <= 0.0f) {
+ synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);
+ return false;
+ }
+ if (initialisedSampleRate == 0.0f) {
+ initMT32ConstantTables(synth);
+ }
+ if (initialisedSampleRate != sampleRate) {
+ initFiltCoeff(sampleRate);
+ initEnvelopes(sampleRate);
+ for (int key = 12; key <= 108; key++) {
+ initDep(&keyLookups[key - 12], (float)key);
+ }
+ }
+ if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) {
+ freeNotes();
+ if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) {
+ return false;
+ }
+ initialisedSampleRate = sampleRate;
+ initialisedMasterTune = masterTune;
+ }
+ return true;
+}
+
+}
diff --git a/engines/sci/sfx/softseq/mt32/tables.h b/engines/sci/sfx/softseq/mt32/tables.h
new file mode 100644
index 0000000000..d9af5114b2
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/tables.h
@@ -0,0 +1,116 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_TABLES_H
+#define MT32EMU_TABLES_H
+
+namespace MT32Emu {
+
+// Mathematical constants
+const double DOUBLE_PI = 3.1415926535897932384626433832795;
+const double DOUBLE_LN = 2.3025850929940456840179914546844;
+const float FLOAT_PI = 3.1415926535897932384626433832795f;
+const float FLOAT_LN = 2.3025850929940456840179914546844f;
+
+// Filter settings
+const int FILTERGRAN = 512;
+
+// Amplitude of waveform generator
+// FIXME: This value is the amplitude possible whilst avoiding
+// overdriven values immediately after filtering when playing
+// back SQ3MT.MID. Needs to be checked.
+const int WGAMP = 12382;
+
+const int MIDDLEC = 60;
+const int MIDDLEA = 69; // By this I mean "A above middle C"
+
+// FIXME:KG: may only need to do 12 to 108
+// 12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
+// and adjustment for timbre pitch, so the results can be outside that range.
+// Should we move it (by octave) into the 12..108 range, or keep it in 0..127 range,
+// or something else altogether?
+const int LOWEST_NOTE = 12;
+const int HIGHEST_NOTE = 127;
+const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT
+
+class Synth;
+
+struct NoteLookup {
+ Bit32u div2;
+ Bit32u *wavTable;
+ Bit32s sawTable[101];
+ int filtTable[2][201];
+ int nfiltTable[101][101];
+ Bit16s *waveforms[3];
+ Bit32u waveformSize[3];
+};
+
+struct KeyLookup {
+ Bit32s envTimeMult[5]; // For envelope time adjustment for key pressed
+ Bit32s envDepthMult[5];
+};
+
+class Tables {
+ float initialisedSampleRate;
+ float initialisedMasterTune;
+ void initMT32ConstantTables(Synth *synth);
+ static Bit16s clampWF(Synth *synth, const char *n, float ampVal, double input);
+ static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div2, File *file);
+ bool initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float tuning);
+ void initEnvelopes(float sampleRate);
+ void initFiltCoeff(float samplerate);
+public:
+ // Constant LUTs
+ Bit32s tvfKeyfollowMult[217];
+ Bit32s tvfVelfollowMult[128][101];
+ Bit32s tvfBiasMult[15][128];
+ Bit32u tvaVelfollowMult[128][101];
+ Bit32s tvaBiasMult[13][128];
+ Bit16s noiseBuf[MAX_SAMPLE_OUTPUT];
+ Bit16s sintable[65536];
+ Bit32s pitchEnvVal[16][101];
+ Bit32s envTimeVelfollowMult[5][128];
+ Bit32s pwVelfollowAdd[15][128];
+ float resonanceFactor[31];
+ Bit32u lfoShift[101][101];
+ Bit32s pwFactor[101];
+ Bit32s volumeMult[101];
+
+ // LUTs varying with sample rate
+ Bit32u envTime[101];
+ Bit32u envDeltaMaxTime[101];
+ Bit32u envDecayTime[101];
+ Bit32u lfoPeriod[101];
+ float filtCoeff[FILTERGRAN][31][8];
+
+ // Various LUTs for each note and key
+ NoteLookup noteLookups[NUM_NOTES];
+ KeyLookup keyLookups[97];
+
+ Tables();
+ bool init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune);
+ File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry *pcmWaves, File *file);
+ void freeNotes();
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/opl2.c b/engines/sci/sfx/softseq/opl2.c
new file mode 100644
index 0000000000..ed2f3001c8
--- /dev/null
+++ b/engines/sci/sfx/softseq/opl2.c
@@ -0,0 +1,718 @@
+/***************************************************************************
+ opl2.c Copyright (C) 2002/04 Solomon Peachy, Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include <resource.h>
+#include <sfx_iterator.h>
+#include "../softseq.h"
+#include "../adlib.h"
+#include <math.h>
+
+#include "fmopl.h"
+
+#ifdef _DREAMCAST
+#define SAMPLE_RATE 22050
+#define CHANNELS SFX_PCM_MONO
+#define STEREO 0
+#else
+#define SAMPLE_RATE 44100
+#define CHANNELS SFX_PCM_STEREO_LR
+#define STEREO 1
+#endif
+
+
+/* local function declarations */
+
+static void opl2_allstop(sfx_softseq_t *self);
+
+//#define DEBUG_ADLIB
+
+/* portions shamelessly lifted from claudio's XMP */
+/* other portions lifted from sound/opl3.c in the Linux kernel */
+
+#define ADLIB_LEFT 0
+#define ADLIB_RIGHT 1
+
+/* #define OPL_INTERNAL_FREQ 3600000 */
+#define OPL_INTERNAL_FREQ 3579545
+
+static int ready = 0;
+static int pcmout_stereo = STEREO;
+
+static int register_base[11] = {
+ 0x20, 0x23, 0x40, 0x43,
+ 0x60, 0x63, 0x80, 0x83,
+ 0xe0, 0xe3, 0xc0
+};
+
+static int register_offset[12] = {
+ /* Channel 1 2 3 4 5 6 7 8 9 */
+ /* Operator 1 */ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12, 0x18, 0x19, 0x1A
+
+};
+
+static int ym3812_note[13] = {
+ 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca,
+ 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287,
+ 0x2ae
+};
+
+static guint8 sci_adlib_vol_base[16] = {
+ 0x00, 0x11, 0x15, 0x19, 0x1D, 0x22, 0x26, 0x2A,
+ 0x2E, 0x23, 0x37, 0x3B, 0x3F, 0x3F, 0x3F, 0x3F
+};
+static guint8 sci_adlib_vol_tables[16][64];
+
+/* ripped out of the linux kernel, of all places. */
+static gint8 fm_volume_table[128] = {
+ -64, -48, -40, -35, -32, -29, -27, -26,
+ -24, -23, -21, -20, -19, -18, -18, -17,
+ -16, -15, -15, -14, -13, -13, -12, -12,
+ -11, -11, -10, -10, -10, -9, -9, -8,
+ -8, -8, -7, -7, -7, -6, -6, -6,
+ -5, -5, -5, -5, -4, -4, -4, -4,
+ -3, -3, -3, -3, -2, -2, -2, -2,
+ -2, -1, -1, -1, -1, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 4,
+ 4, 4, 4, 4, 4, 4, 4, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 8, 8, 8, 8, 8
+};
+
+/* back to your regularly scheduled definitions */
+
+static guint8 instr[MIDI_CHANNELS];
+static guint16 pitch[MIDI_CHANNELS];
+static guint8 vol[MIDI_CHANNELS];
+static guint8 pan[MIDI_CHANNELS];
+static int free_voices = ADLIB_VOICES;
+static guint8 oper_note[ADLIB_VOICES];
+static guint8 oper_chn[ADLIB_VOICES];
+
+static FM_OPL *ym3812_L = NULL;
+static FM_OPL *ym3812_R = NULL;
+
+static guint8 adlib_reg_L[256];
+static guint8 adlib_reg_R[256];
+static guint8 adlib_master;
+
+
+/* initialise note/operator lists, etc. */
+void adlibemu_init_lists(void)
+{
+ int i;
+
+ int j;
+
+ for (i = 0 ; i < 16 ; i++) {
+ for (j = 0; j < 64 ; j++) {
+ sci_adlib_vol_tables[i][j] = ((guint16)sci_adlib_vol_base[i]) * j / 63;
+ }
+ }
+
+ for (i = 0; i < MIDI_CHANNELS ; i++) {
+ pitch[i] = 8192; /* center the pitch wheel */
+ }
+
+ free_voices = ADLIB_VOICES;
+
+ memset(instr, 0, sizeof(instr));
+ memset(vol, 0x7f, sizeof(vol));
+ memset(pan, 0x3f, sizeof(pan));
+ memset(adlib_reg_L, 0, sizeof(adlib_reg_L));
+ memset(adlib_reg_R, 0, sizeof(adlib_reg_R));
+ memset(oper_chn, 0xff, sizeof(oper_chn));
+ memset(oper_note, 0xff, sizeof(oper_note));
+ adlib_master=12;
+}
+
+/* more shamelessly lifted from xmp and adplug. And altered. :) */
+
+static inline int opl_write_L (int a, int v)
+{
+ adlib_reg_L[a] = v;
+ OPLWrite (ym3812_L, 0x388, a);
+ return OPLWrite (ym3812_L, 0x389, v);
+}
+
+static inline int opl_write_R (int a, int v)
+{
+ adlib_reg_R[a] = v;
+ OPLWrite (ym3812_R, 0x388, a);
+ return OPLWrite (ym3812_R, 0x389, v);
+}
+
+static inline int opl_write(int a, int v)
+{
+ opl_write_L(a,v);
+ return opl_write_R(a,v);
+}
+
+/*
+static inline guint8 opl_read (int a)
+{
+ OPLWrite (ym3812_L, 0x388, a);
+ return OPLRead (ym3812_L, 0x389);
+}
+*/
+
+void synth_setpatch (int voice, guint8 *data)
+{
+ int i;
+
+ opl_write(0xBD, 0);
+
+ for (i = 0; i < 10; i++)
+ opl_write(register_base[i] + register_offset[voice], data[i]);
+
+ opl_write(register_base[10] + voice, data[10]);
+
+ /* mute voice after patch change */
+ opl_write_L(0xb0 + voice, adlib_reg_L[0xb0+voice] & 0xdf);
+ opl_write_R(0xb0 + voice, adlib_reg_R[0xb0+voice] & 0xdf);
+
+#ifdef DEBUG_ADLIB
+ for (i = 0; i < 10; i++)
+ printf("%02x ", adlib_reg_L[register_base[i]+register_offset[voice]]);
+ printf("%02x ", adlib_reg_L[register_base[10]+voice]);
+#endif
+
+}
+
+void synth_setvolume_L (int voice, int volume)
+{
+ gint8 level1, level2;
+
+ level1 = ~adlib_reg_L[register_base[2]+register_offset[voice]] & 0x3f;
+ level2 = ~adlib_reg_L[register_base[3]+register_offset[voice]] & 0x3f;
+
+ if (level1) {
+ level1 += sci_adlib_vol_tables[adlib_master][volume>>1];
+ }
+
+ if (level2) {
+ level2 += sci_adlib_vol_tables[adlib_master][volume>>1];
+ }
+
+ if (level1 > 0x3f)
+ level1 = 0x3f;
+ if (level1 < 0)
+ level1 = 0;
+
+ if (level2 > 0x3f)
+ level2 = 0x3f;
+ if (level2 < 0)
+ level2 = 0;
+
+ /* algorithm-dependent; we may need to set both operators. */
+ if (adlib_reg_L[register_base[10]+voice] & 1)
+ opl_write_L(register_base[2]+register_offset[voice],
+ (guint8)((~level1 &0x3f) |
+ (adlib_reg_L[register_base[2]+register_offset[voice]]&0xc0)));
+
+ opl_write_L(register_base[3]+register_offset[voice],
+ (guint8)((~level2 &0x3f) |
+ (adlib_reg_L[register_base[3]+register_offset[voice]]&0xc0)));
+
+}
+
+void synth_setvolume_R (int voice, int volume)
+{
+ gint8 level1, level2;
+
+ level1 = ~adlib_reg_R[register_base[2]+register_offset[voice]] & 0x3f;
+ level2 = ~adlib_reg_R[register_base[3]+register_offset[voice]] & 0x3f;
+
+ if (level1) {
+ level1 += sci_adlib_vol_tables[adlib_master][volume>>1];
+ }
+
+ if (level2) {
+ level2 += sci_adlib_vol_tables[adlib_master][volume>>1];
+ }
+
+ if (level1 > 0x3f)
+ level1 = 0x3f;
+ if (level1 < 0)
+ level1 = 0;
+
+ if (level2 > 0x3f)
+ level2 = 0x3f;
+ if (level2 < 0)
+ level2 = 0;
+
+ /* now for the other side. */
+ if (adlib_reg_R[register_base[10]+voice] & 1)
+ opl_write_R(register_base[2]+register_offset[voice],
+ (guint8)((~level1 &0x3f) |
+ (adlib_reg_R[register_base[2]+register_offset[voice]]&0xc0)));
+
+ opl_write_R(register_base[3]+register_offset[voice],
+ (guint8)((~level2 &0x3f) |
+ (adlib_reg_R[register_base[3]+register_offset[voice]]&0xc0)));
+
+}
+
+void synth_setnote (int voice, int note, int bend)
+{
+ int n, fre, oct;
+ float delta;
+
+ delta = 0;
+
+ n = note % 12;
+
+ if (bend < 8192)
+ bend = 8192-bend;
+ delta = pow(2, (float) (bend%8192)/8192.0);
+
+ if (bend > 8192)
+ fre = ym3812_note[n]*delta; else
+ fre = ym3812_note[n]/delta;
+
+ oct = note / 12 - 1;
+
+ if (oct < 0)
+ oct = 0;
+
+ opl_write(0xa0 + voice, fre & 0xff);
+ opl_write(0xb0 + voice,
+ 0x20 | ((oct << 2) & 0x1c) | ((fre >> 8) & 0x03));
+#ifdef DEBUG_ADLIB
+ printf("-- %02x %02x\n", adlib_reg_L[0xa0+voice], adlib_reg_L[0xb0+voice]);
+#endif
+
+}
+
+
+/* back to your regularly scheduled driver */
+
+int adlibemu_stop_note(int chn, int note, int velocity)
+{
+ int i, op=255;
+
+ // sciprintf("Note off %d %d %d\n", chn, note, velocity);
+
+ for (i=0;i<ADLIB_VOICES && op==255;i++) {
+ if (oper_chn[i] == chn)
+ if (oper_note[i] == note)
+ op=i;
+ }
+
+ if (op==255) {
+#ifdef DEBUG_ADLIB
+ printf ("ADLIB: can't stop note: C%02x N%02x V%02x\n", chn, note, velocity);
+ printf ("C ");
+ for (i = 0; i < ADLIB_VOICES ; i++ ) {
+ printf ("%02x ", oper_chn[i]);
+ }
+ printf ("\n");
+ printf ("N ");
+ for (i = 0; i < ADLIB_VOICES ; i++ ) {
+ printf ("%02x ", oper_note[i]);
+ }
+ printf ("\n");
+#endif
+ return -1; /* that note isn't playing.. */
+ }
+
+ opl_write_L(0xb0+op,(adlib_reg_L[0xb0+op] & 0xdf));
+ opl_write_R(0xb0+op,(adlib_reg_R[0xb0+op] & 0xdf));
+
+ oper_chn[op] = 255;
+ oper_note[op] = 255;
+
+ free_voices++;
+
+#ifdef DEBUG_ADLIB
+ printf("stop voice %d (%d rem): C%02x N%02x V%02x\n", op, free_voices, chn, note, velocity);
+#endif
+
+ return 0;
+}
+
+int adlibemu_start_note(int chn, int note, int velocity)
+{
+ int op, volume_L, volume_R, inst = 0;
+
+ // sciprintf("Note on %d %d %d\n", chn, note, velocity);
+
+ if (velocity == 0) {
+ return adlibemu_stop_note(chn, note, velocity);
+ }
+
+ if (free_voices <= 0) {
+ printf("ADLIB: All voices full\n"); /* XXX implement overflow code */
+ return -1;
+ }
+
+ for (op = 0; op < ADLIB_VOICES ; op++)
+ if (oper_chn[op] == 255)
+ break;
+
+ if (op == ADLIB_VOICES) {
+ printf("ADLIB: WTF? We couldn't find a voice yet it we have %d left.\n", free_voices);
+ return -1;
+ }
+
+ /* Scale channel volume */
+ volume_L = velocity * vol[chn] / 127;
+ volume_R = velocity * vol[chn] / 127;
+
+ /* Apply a pan */
+ if (pcmout_stereo) {
+ if (pan[chn] > 0x3f) /* pan right; so we scale the left down. */
+ volume_L = volume_L / 0x3f * (0x3f - (pan[chn] - 0x3f));
+ else if (pan[chn] < 0x3f) /* pan left; so we scale the right down.*/
+ volume_R = volume_R / 0x3f * (0x3f - (0x3f-pan[chn]));
+ }
+ inst = instr[chn];
+
+ synth_setpatch(op, adlib_sbi[inst]);
+ synth_setvolume_L(op, volume_L);
+ synth_setvolume_R(op, volume_R);
+ synth_setnote(op, note, pitch[chn]);
+
+ oper_chn[op] = chn;
+ oper_note[op] = note;
+ free_voices--;
+
+#ifdef DEBUG_ADLIB
+ printf("play voice %d (%d rem): C%02x N%02x V%02x/%02x-%02x P%02x (%02x/%02x)\n", op, free_voices, chn, note, velocity, volume_L, volume_R, inst,
+ adlib_reg_L[register_base[2]+register_offset[op]] & 0x3f,
+ adlib_reg_L[register_base[3]+register_offset[op]] & 0x3f);
+#endif
+
+ return 0;
+}
+
+static
+void adlibemu_update_pitch(int chn, int note, int newpitch)
+{
+ int i;
+ int matched = 0;
+
+ pitch[chn] = newpitch;
+
+ for (i=0;i<ADLIB_VOICES;i++) {
+ if (oper_chn[i] == chn)
+ {
+ matched++;
+ synth_setnote(i, oper_note[i], newpitch);
+ }
+ }
+
+// printf("Matched %d notes on channel %d.\n", matched, chn);
+}
+
+void test_adlib (void) {
+
+ int voice = 0;
+#if 0
+ guint8 data[] = { 0x25, 0x21, 0x48, 0x48, 0xf0, 0xf2, 0xf0, 0xa5, 0x00, 0x00, 0x06 };
+#else
+ guint8 *data = adlib_sbi[0x0a];
+#endif
+
+#if 1
+ opl_write(register_base[0]+register_offset[voice], data[0]);
+ opl_write(register_base[1]+register_offset[voice], data[1]);
+ opl_write(register_base[2]+register_offset[voice], data[2]);
+ opl_write(register_base[3]+register_offset[voice], data[3]);
+ opl_write(register_base[4]+register_offset[voice], data[4]);
+ opl_write(register_base[5]+register_offset[voice], data[5]);
+ opl_write(register_base[6]+register_offset[voice], data[6]);
+ opl_write(register_base[7]+register_offset[voice], data[7]);
+ opl_write(register_base[8]+register_offset[voice], data[8]);
+ opl_write(register_base[9]+register_offset[voice], data[9]);
+ opl_write(register_base[10]+register_offset[voice], data[10]);
+#else
+ synth_setpatch(voice, data);
+#endif
+
+#if 0
+ opl_write(0xA0 + voice, 0x57);
+ opl_write(0xB0 + voice, 0x2d);
+#else
+ synth_setvolume_L(voice, 0x50);
+ synth_setvolume_R(voice, 0x50);
+ synth_setnote(voice, 0x30, 0);
+#endif
+
+ /*
+ instr[0x0e] = 0x0a;
+ instr[0x03] = 0x26;
+
+ adlibemu_start_note(0x0e, 0x30, 0x40);
+ sleep(1);
+ adlibemu_start_note(0x03, 0x48, 0x40);
+ sleep(1);
+ */
+}
+
+
+/* count is # of FRAMES, not bytes.
+ We assume 16-bit stereo frames (ie 4 bytes)
+*/
+static void
+opl2_poll (sfx_softseq_t *self, byte *dest, int count)
+{
+ gint16 *buffer = (gint16 *) dest;
+ gint16 *ptr = buffer;
+
+ if (!ready) {
+ fprintf(stderr, "synth_mixer(): !ready \n");
+ exit(1);
+ }
+
+ if (!buffer) {
+ fprintf(stderr, "synth_mixer(): !buffer \n");
+ exit(1);
+ }
+
+#if 0
+ {
+ static unsigned long remaining_delta = 0;
+ int samples;
+ int remaining = count;
+
+ while (remaining > 0) {
+ samples = remaining_delta * pcmout_sample_rate / 1000000;
+ samples = sci_min(samples, remaining);
+ if (samples) {
+ YM3812UpdateOne(ADLIB_LEFT, ptr, samples, 1);
+ YM3812UpdateOne(ADLIB_RIGHT, ptr+1, samples, 1);
+ }
+ if (remaining > samples) {
+ remaining_delta = (remaining - samples) * 1000000 / pcmout_sample_rate;
+ } else {
+ song->play_next_note();
+ remaining_delta = song->get_next_delta();
+ song->advance();
+ }
+ remaining -= samples;
+ }
+ }
+#endif
+
+ if (pcmout_stereo) {
+ YM3812UpdateOne (ym3812_L, ptr, count, 1);
+ YM3812UpdateOne (ym3812_R, ptr+1, count, 1);
+ } else {
+ YM3812UpdateOne (ym3812_L, ptr, count, 0);
+ }
+}
+
+static int
+opl2_init(sfx_softseq_t *self, byte *data_ptr, int data_length, byte *data2_ptr,
+ int data2_length)
+{
+ int i;
+
+ /* load up the patch.003 file, parse out the instruments */
+ if (data_length < 1344) {
+ sciprintf ("[sfx:seq:opl2] Invalid patch.003: Expected %d, got %d\n", 1344, data_length);
+ return -1;
+ }
+
+ for (i = 0; i < 48; i++)
+ make_sbi((adlib_def *)(data_ptr+(28 * i)), adlib_sbi[i]);
+
+ if (data_length > 1344)
+ for (i = 48; i < 96; i++)
+ make_sbi((adlib_def *)(data_ptr+2+(28 * i)), adlib_sbi[i]);
+
+ OPLBuildTables(FMOPL_ENV_BITS_HQ, FMOPL_EG_ENT_HQ);
+
+ if (!(ym3812_L = OPLCreate (OPL_TYPE_YM3812, OPL_INTERNAL_FREQ, SAMPLE_RATE)) ||
+ !(ym3812_R = OPLCreate (OPL_TYPE_YM3812, OPL_INTERNAL_FREQ, SAMPLE_RATE))) {
+ sciprintf ("[sfx:seq:opl2] Failure: Emulator init failed!\n");
+ return SFX_ERROR;
+ }
+
+ ready = 1;
+
+ opl2_allstop(self);
+ return SFX_OK;
+}
+
+
+static void
+opl2_exit(sfx_softseq_t *self)
+{
+ FM_OPL *opl = ym3812_L;
+ ym3812_L = NULL;
+ OPLDestroy(opl);
+ opl = ym3812_R;
+ ym3812_R = NULL;
+ OPLDestroy(opl);
+
+ // XXX deregister with pcm layer.
+}
+
+static void
+opl2_allstop(sfx_softseq_t *self)
+{
+ // printf("AdlibEmu: Reset\n");
+ if (! ready)
+ return;
+
+ adlibemu_init_lists();
+
+ OPLResetChip (ym3812_L);
+ OPLResetChip (ym3812_R);
+
+ opl_write(0x01, 0x20);
+ opl_write(0xBD, 0xc0);
+
+#ifdef DEBUG_ADLIB
+ printf("ADLIB: reset complete\n");
+#endif
+ // test_adlib();
+}
+
+int midi_adlibemu_reverb(short param)
+{
+ printf("ADLIB: reverb NYI %04x \n", param);
+ return 0;
+}
+
+int midi_adlibemu_event(guint8 command, guint8 note, guint8 velocity, guint32 delta)
+{
+ guint8 channel, oper;
+
+ channel = command & 0x0f;
+ oper = command & 0xf0;
+
+ switch (oper) {
+ case 0x80:
+ return adlibemu_stop_note(channel, note, velocity);
+ case 0x90: /* noteon and noteoff */
+ return adlibemu_start_note(channel, note, velocity);
+ case 0xe0: /* Pitch bend */
+ {
+ int bend = (note & 0x7f) | ((velocity & 0x7f) << 7);
+// printf("Bend to %d\n", bend);
+ adlibemu_update_pitch(channel, note, bend);
+ }
+ case 0xb0: /* CC changes. */
+ switch (note) {
+ case 0x07:
+ vol[channel] = velocity;
+ break;
+ case 0x0a:
+ pan[channel] = velocity;
+ break;
+ case 0x4b:
+ break;
+ case 0x7b: { /* all notes off */
+ int i = 0;
+ for (i=0;i<ADLIB_VOICES;i++)
+ if (oper_chn[i] == channel)
+ adlibemu_stop_note(channel, oper_note[i], 0);
+ break;
+ }
+ default:
+ ; /* XXXX ignore everything else for now */
+ }
+ return 0;
+ case 0xd0: /* aftertouch */
+ /* XXX Aftertouch in the OPL thing? */
+ return 0;
+ default:
+ printf("ADLIB: Unknown event %02x\n", command);
+ return 0;
+ }
+
+ return 0;
+}
+
+int midi_adlibemu_event2(guint8 command, guint8 param, guint32 delta)
+{
+ guint8 channel;
+ guint8 oper;
+
+ channel = command & 0x0f;
+ oper = command & 0xf0;
+ switch (oper) {
+ case 0xc0: /* change instrument */
+#ifdef DEBUG_ADLIB
+ printf("ADLIB: Selecting instrument %d on channel %d\n", param, channel);
+#endif
+ instr[channel] = param;
+ return 0;
+ default:
+ printf("ADLIB: Unknown event %02x\n", command);
+ }
+
+ return 0;
+}
+
+static void
+opl2_volume(sfx_softseq_t *self, int volume)
+{
+ guint8 i;
+
+ i = (guint8)volume * 15 / 100;
+
+ adlib_master=i;
+
+#ifdef DEBUG_ADLIB
+ printf("ADLIB: master volume set to %d\n", adlib_master);
+#endif
+}
+
+int
+opl2_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+void
+opl2_event(sfx_softseq_t *self, byte cmd, int argc, byte *argv)
+{
+ if (argc == 1)
+ midi_adlibemu_event2(cmd, argv[0], 0);
+ else if (argc == 2)
+ midi_adlibemu_event(cmd, argv[0], argv[1], 0);
+}
+
+/* the driver struct */
+
+sfx_softseq_t sfx_softseq_opl2 = {
+ "adlibemu",
+ "0.1",
+ opl2_set_option,
+ opl2_init,
+ opl2_exit,
+ opl2_volume,
+ opl2_event,
+ opl2_poll,
+ opl2_allstop,
+ NULL,
+ 3, /* use patch.003 */
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x4, /* Play flags */
+ 0, /* No rhythm channel (9) */
+ ADLIB_VOICES, /* # of voices */
+ {SAMPLE_RATE, CHANNELS, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/pcspeaker.c b/engines/sci/sfx/softseq/pcspeaker.c
new file mode 100644
index 0000000000..771c8f0da5
--- /dev/null
+++ b/engines/sci/sfx/softseq/pcspeaker.c
@@ -0,0 +1,184 @@
+/***************************************************************************
+ soft-pcspeaker.c Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* PC speaker software sequencer for FreeSCI */
+
+#include "../softseq.h"
+#include <sci_midi.h>
+
+#define FREQUENCY 94020
+
+static int volume = 0x0600;
+static int note = 0; /* Current halftone, or 0 if off */
+static int freq_count = 0;
+
+extern sfx_softseq_t sfx_softseq_pcspeaker;
+/* Forward-declare the sequencer we are defining here */
+
+
+static int
+sps_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+sps_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2,
+ int patch2_len)
+{
+ return SFX_OK;
+}
+
+static void
+sps_exit(sfx_softseq_t *self)
+{
+}
+
+static void
+sps_event(sfx_softseq_t *self, byte command, int argc, byte *argv)
+{
+#if 0
+ fprintf(stderr, "Note [%02x : %02x %02x]\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0);
+#endif
+
+ switch (command & 0xf0) {
+
+ case 0x80:
+ if (argv[0] == note)
+ note = 0;
+ break;
+
+ case 0x90:
+ if (!argv[1]) {
+ if (argv[0] == note)
+ note = 0;
+ } else
+ note = argv[0];
+ /* Ignore velocity otherwise; we just use the global one */
+ break;
+
+ case 0xb0:
+ if (argv[1] == SCI_MIDI_CHANNEL_NOTES_OFF)
+ note = 0;
+ break;
+
+ default:
+#if DEBUG
+ fprintf(stderr, "[SFX:PCM-PC] Unused MIDI command %02x %02x %02x\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0);
+#endif
+ break; /* ignore */
+ }
+}
+
+#define BASE_NOTE 129 /* A10 */
+#define BASE_OCTAVE 10 /* A10, as I said */
+
+static int
+freq_table[12] = { /* A4 is 440Hz, halftone map is x |-> ** 2^(x/12) */
+ 28160, /* A10 */
+ 29834,
+ 31608,
+ 33488,
+ 35479,
+ 37589,
+ 39824,
+ 42192,
+ 44701,
+ 47359,
+ 50175,
+ 53159
+};
+
+
+void
+sps_poll(sfx_softseq_t *self, byte *dest, int len)
+{
+ int halftone_delta = note - BASE_NOTE;
+ int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE;
+ int halftone_index = (halftone_delta + (12*100)) % 12 ;
+ int freq = (!note)? 0 : freq_table[halftone_index] / (1 << (-oct_diff));
+ gint16 *buf = (gint16 *) dest;
+
+ int i;
+ for (i = 0; i < len; i++) {
+ if (note) {
+ freq_count += freq;
+ while (freq_count >= (FREQUENCY << 1))
+ freq_count -= (FREQUENCY << 1);
+
+ if (freq_count - freq < 0) {
+ /* Unclean rising edge */
+ int l = volume << 1;
+ buf[i] = -volume + (l*freq_count)/freq;
+ } else if (freq_count >= FREQUENCY
+ && freq_count - freq < FREQUENCY) {
+ /* Unclean falling edge */
+ int l = volume << 1;
+ buf[i] = volume - (l*(freq_count - FREQUENCY))/freq;
+ } else {
+ if (freq_count < FREQUENCY)
+ buf[i] = volume;
+ else
+ buf[i] = -volume;
+ }
+ } else
+ buf[i] = 0;
+ }
+
+}
+
+void
+sps_volume(sfx_softseq_t *self, int new_volume)
+{
+ volume = new_volume << 4;
+}
+
+void
+sps_allstop(sfx_softseq_t *self)
+{
+ note = 0;
+}
+
+sfx_softseq_t sfx_softseq_pcspeaker = {
+ "pc-speaker",
+ "0.3",
+ sps_set_option,
+ sps_init,
+ sps_exit,
+ sps_volume,
+ sps_event,
+ sps_poll,
+ sps_allstop,
+ NULL,
+ SFX_SEQ_PATCHFILE_NONE,
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x20, /* PC speaker channel only */
+ 0, /* No rhythm channel */
+ 1, /* # of voices */
+ {FREQUENCY, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/softsequencers.c b/engines/sci/sfx/softseq/softsequencers.c
new file mode 100644
index 0000000000..d0f544190e
--- /dev/null
+++ b/engines/sci/sfx/softseq/softsequencers.c
@@ -0,0 +1,71 @@
+/***************************************************************************
+ softsequencers.c Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "../softseq.h"
+
+extern sfx_softseq_t sfx_softseq_opl2;
+extern sfx_softseq_t sfx_softseq_SN76496;
+extern sfx_softseq_t sfx_softseq_pcspeaker;
+extern sfx_softseq_t sfx_softseq_amiga;
+extern sfx_softseq_t sfx_softseq_mt32;
+extern sfx_softseq_t sfx_softseq_fluidsynth;
+
+static sfx_softseq_t *sw_sequencers[] = {
+ &sfx_softseq_opl2,
+/* &sfx_softseq_mt32, */
+ &sfx_softseq_SN76496,
+ &sfx_softseq_pcspeaker,
+ &sfx_softseq_amiga,
+#ifdef HAVE_FLUIDSYNTH_H
+ &sfx_softseq_fluidsynth,
+#endif
+ NULL
+};
+
+
+sfx_softseq_t *
+sfx_find_softseq(char *name)
+{
+ if (!name)
+ return sw_sequencers[0];
+ else {
+ int i = 0;
+ while (sw_sequencers[i])
+ if (!strcmp(sw_sequencers[i]->name, name))
+ return sw_sequencers[i];
+ else
+ ++i;
+
+ return NULL; /* Not found */
+ }
+}