aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/midi/adlib.cpp1366
-rw-r--r--common/gameDetector.cpp9
-rw-r--r--scumm/imuse.cpp65
-rw-r--r--scumm/resource.cpp9
-rw-r--r--scumm/scummvm.cpp4
-rw-r--r--sound/mididrv.cpp75
-rw-r--r--sound/mididrv.h8
-rw-r--r--sound/midistreamer.cpp34
8 files changed, 1529 insertions, 41 deletions
diff --git a/backends/midi/adlib.cpp b/backends/midi/adlib.cpp
new file mode 100644
index 0000000000..a831c9f062
--- /dev/null
+++ b/backends/midi/adlib.cpp
@@ -0,0 +1,1366 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001/2002 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ */
+
+/*
+ * Timidity support by Lionel Ulmer <lionel.ulmer@free.fr>
+ * QuickTime support by Florent Boudet <flobo@ifrance.com>
+ * Raw output support by Michael Pearce
+ * MorphOS support by Ruediger Hanke
+ * Alsa support by Nicolas Noble <nicolas@nobis-crew.org> copied from
+ * both the QuickTime support and (vkeybd http://www.alsa-project.org/~iwai/alsa.html)
+ */
+
+#include "stdafx.h"
+#include "mididrv.h"
+#include "fmopl.h"
+#include "mixer.h"
+#include "scumm.h"
+#include "imuse.h"
+#include "common/engine.h" // for warning/error/debug
+#include "common/util.h"
+
+class MidiDriver_ADLIB;
+struct MidiChannelAdl;
+
+struct InstrumentExtra {
+ byte a, b, c, d, e, f, g, h;
+};
+
+struct Instrument {
+ byte flags_1;
+ byte oplvl_1;
+ byte atdec_1;
+ byte sustrel_1;
+ byte waveform_1;
+ byte flags_2;
+ byte oplvl_2;
+ byte atdec_2;
+ byte sustrel_2;
+ byte waveform_2;
+ byte feedback;
+ byte flags_a;
+ InstrumentExtra extra_a;
+ byte flags_b;
+ InstrumentExtra extra_b;
+ byte duration;
+};
+
+struct AdlibPart {
+ byte _chan;
+ AdlibPart *_prev, *_next;
+ MidiDriver_ADLIB *_drv;
+ MidiChannelAdl *_mc;
+ int16 _pitchbend;
+ byte _pitchbend_factor;
+ int8 _transpose_eff;
+ byte _vol_eff;
+ int8 _detune_eff;
+ byte _modwheel;
+ bool _pedal;
+ byte _program;
+ byte _pri_eff;
+ Instrument _part_instr;
+};
+
+struct Struct10 {
+ byte active;
+ int16 cur_val;
+ int16 count;
+ uint16 param;
+ int16 start_value;
+ byte loop;
+ byte table_a[4];
+ byte table_b[4];
+ int8 unk3;
+ int8 modwheel;
+ int8 modwheel_last;
+ uint16 speed_lo_max;
+ uint16 num_steps;
+ int16 speed_hi;
+ int8 direction;
+ uint16 speed_lo;
+ uint16 speed_lo_counter;
+};
+
+struct Struct11 {
+ int16 modify_val;
+ byte param, flag0x40, flag0x10;
+ Struct10 *s10;
+};
+
+struct MidiChannelAdl {
+ AdlibPart *_part;
+ MidiChannelAdl *_next, *_prev;
+ byte _waitforpedal;
+ byte _note;
+ byte _channel;
+ byte _twochan;
+ byte _vol_1, _vol_2;
+ int16 _duration;
+
+ Struct10 _s10a;
+ Struct11 _s11a;
+ Struct10 _s10b;
+ Struct11 _s11b;
+
+ MidiChannelAdl() : _part (0), _next(0), _prev(0) {}
+};
+
+struct AdlibSetParams {
+ byte a, b, c, d;
+};
+
+static const byte channel_mappings[9] = {
+ 0, 1, 2, 8,
+ 9, 10, 16, 17,
+ 18
+};
+
+static const byte channel_mappings_2[9] = {
+ 3, 4, 5, 11,
+ 12, 13, 19, 20,
+ 21
+};
+
+static const AdlibSetParams adlib_setparam_table[] = {
+ {0x40, 0, 63, 63}, // level
+ {0xE0, 2, 0, 0}, // unused
+ {0x40, 6, 192, 0}, // level key scaling
+ {0x20, 0, 15, 0}, // modulator frequency multiple
+ {0x60, 4, 240, 15}, // attack rate
+ {0x60, 0, 15, 15}, // decay rate
+ {0x80, 4, 240, 15}, // sustain level
+ {0x80, 0, 15, 15}, // release rate
+ {0xE0, 0, 3, 0}, // waveform select
+ {0x20, 7, 128, 0}, // amp mod
+ {0x20, 6, 64, 0}, // vib
+ {0x20, 5, 32, 0}, // eg typ
+ {0x20, 4, 16, 0}, // ksr
+ {0xC0, 0, 1, 0}, // decay alg
+ {0xC0, 1, 14, 0} // feedback
+};
+
+const byte param_table_1[16] = {
+ 29, 28, 27, 0,
+ 3, 4, 7, 8,
+ 13, 16, 17, 20,
+ 21, 30, 31, 0
+};
+
+const uint16 param_table_2[16] = {
+ 0x2FF, 0x1F, 0x7, 0x3F,
+ 0x0F, 0x0F, 0x0F, 0x3,
+ 0x3F, 0x0F, 0x0F, 0x0F,
+ 0x3, 0x3E, 0x1F, 0
+};
+
+static const uint16 num_steps_table[] = {
+ 1, 2, 4, 5,
+ 6, 7, 8, 9,
+ 10, 12, 14, 16,
+ 18, 21, 24, 30,
+ 36, 50, 64, 82,
+ 100, 136, 160, 192,
+ 240, 276, 340, 460,
+ 600, 860, 1200, 1600
+};
+
+static const byte octave_numbers[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7
+};
+
+static const byte note_numbers[] = {
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10
+};
+
+static const byte note_to_f_num[] = {
+ 90, 91, 92, 92, 93, 94, 94, 95,
+ 96, 96, 97, 98, 98, 99, 100, 101,
+ 101, 102, 103, 104, 104, 105, 106, 107,
+ 107, 108, 109, 110, 111, 111, 112, 113,
+ 114, 115, 115, 116, 117, 118, 119, 120,
+ 121, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 157, 158, 159, 160,
+ 161, 162, 163, 165, 166, 167, 168, 169,
+ 171, 172, 173, 174, 176, 177, 178, 180,
+ 181, 182, 184, 185, 186, 188, 189, 190,
+ 192, 193, 194, 196, 197, 199, 200, 202,
+ 203, 205, 206, 208, 209, 211, 212, 214,
+ 215, 217, 218, 220, 222, 223, 225, 226,
+ 228, 230, 231, 233, 235, 236, 238, 240,
+ 242, 243, 245, 247, 249, 251, 252, 254,
+};
+
+static byte map_gm_to_fm [128][30] = {
+// DERIVED FROM DAY OF THE TENTACLE
+ // 00
+{ 0x2F, 0x0B, 0x08, 0x78, 0x16, 0x24, 0x22, 0x0B, 0x9A, 0x34, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x2F, 0x0B, 0x08, 0x78, 0x16, 0x24, 0x22, 0x0B, 0x9A, 0x34, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x2F, 0x0B, 0x08, 0x78, 0x16, 0x24, 0x22, 0x0B, 0x9A, 0x34, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x2F, 0x0B, 0x08, 0x78, 0x16, 0x24, 0x22, 0x0B, 0x9A, 0x34, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE2, 0x00, 0x6C, 0x77, 0x7D, 0xE4, 0x40, 0x7D, 0xA7, 0x66, 0x07, 0xA1, 0x00, 0x02, 0x1E, 0x01, 0x20, 0x01, 0x01, 0x1F, 0xAC, 0x00, 0x04, 0x24, 0x02, 0x21, 0x02, 0x02, 0x21, 0x00 },
+ // 10
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xC9, 0x40, 0x3A, 0x78, 0x5E, 0xC2, 0x00, 0x4D, 0x9A, 0x7C, 0x00, 0xA1, 0xA0, 0x08, 0x1F, 0x05, 0x1E, 0x02, 0x05, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x28, 0x1D, 0x19, 0x68, 0x02, 0x22, 0x35, 0x09, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0x28, 0x1D, 0x19, 0x68, 0x02, 0x22, 0x35, 0x09, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xC6, 0x40, 0x3B, 0x78, 0x5E, 0xC2, 0x00, 0x4D, 0x9A, 0x7C, 0x00, 0xA1, 0xA0, 0x08, 0x1F, 0x05, 0x1E, 0x02, 0x05, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xE2, 0x00, 0x6C, 0x77, 0x7D, 0xE4, 0x40, 0x7D, 0xA7, 0x66, 0x07, 0xA1, 0x00, 0x02, 0x1E, 0x01, 0x20, 0x01, 0x01, 0x1F, 0xAC, 0x00, 0x04, 0x24, 0x02, 0x21, 0x02, 0x02, 0x21, 0x00 },
+{ 0xE2, 0x28, 0x38, 0xE8, 0x02, 0xE6, 0x33, 0x0B, 0xF9, 0x00, 0x08, 0xA1, 0x00, 0x02, 0x1E, 0x02, 0x20, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0xE2, 0x28, 0x38, 0xE8, 0x02, 0xE6, 0x33, 0x0B, 0xF9, 0x00, 0x08, 0xA1, 0x00, 0x02, 0x1E, 0x02, 0x20, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE2, 0x00, 0x6C, 0x77, 0x7D, 0xE4, 0x40, 0x7D, 0xA7, 0x66, 0x07, 0xA1, 0x00, 0x02, 0x1E, 0x01, 0x20, 0x01, 0x01, 0x1F, 0xAC, 0x00, 0x04, 0x24, 0x02, 0x21, 0x02, 0x02, 0x21, 0x00 },
+ // 20
+{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ // 30
+{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
+{ 0x22, 0x19, 0x79, 0x67, 0x00, 0x22, 0x3F, 0x2A, 0xC6, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
+{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
+{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 }, // Extrapolated
+ // 40
+{ 0xE2, 0x15, 0x7B, 0xB5, 0x02, 0xE1, 0x33, 0xAF, 0xF4, 0x36, 0x08, 0xA1, 0x00, 0x04, 0x1E, 0x04, 0x1F, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xF4, 0x40, 0x9F, 0xFA, 0x61, 0xE2, 0x13, 0x7F, 0xFA, 0x7D, 0x02, 0x21, 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x35, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x02, 0x00, 0x67, 0xAA, 0x65, 0x02, 0x64, 0x28, 0xF9, 0x7C, 0x08, 0x81, 0x00, 0x04, 0x1D, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x02, 0x40, 0x04, 0x9A, 0x55, 0xC2, 0x4B, 0x2B, 0xCB, 0x7C, 0x06, 0x41, 0x00, 0x00, 0x20, 0x06, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x02, 0x0C, 0x03, 0x6A, 0x7D, 0x02, 0x00, 0x23, 0xEA, 0x7C, 0x02, 0x81, 0x00, 0x02, 0x20, 0x01, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xF4, 0x40, 0x9F, 0xFA, 0x61, 0xE2, 0x13, 0x7F, 0xFA, 0x7D, 0x02, 0x21, 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x35, 0x00 },
+{ 0xF4, 0x40, 0x9F, 0xFA, 0x61, 0xE2, 0x13, 0x7F, 0xFA, 0x7D, 0x02, 0x21, 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x35, 0x00 },
+ // 50
+{ 0xF4, 0x40, 0x9F, 0xFA, 0x61, 0xE2, 0x13, 0x7F, 0xFA, 0x7D, 0x02, 0x21, 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x35, 0x00 },
+{ 0x02, 0x00, 0x67, 0xAA, 0x65, 0x02, 0x64, 0x28, 0xF9, 0x7C, 0x08, 0x81, 0x00, 0x04, 0x1D, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE1, 0x00, 0xCE, 0xD9, 0x4E, 0xE2, 0x00, 0x8F, 0x99, 0x65, 0x0E, 0x01, 0x00, 0x01, 0x1F, 0x00, 0x1E, 0x01, 0x01, 0x20, 0x01, 0x00, 0x00, 0x1F, 0x06, 0x1E, 0x00, 0x06, 0x1F, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xE2, 0x00, 0xCE, 0xD9, 0x4C, 0xE2, 0x00, 0x8F, 0x99, 0x64, 0x0E, 0x81, 0x10, 0x00, 0x1E, 0x05, 0x1F, 0x12, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x1F, 0x06, 0x1E, 0x00, 0x06, 0x1F, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xF2, 0x00, 0xAF, 0xFA, 0x5C, 0xF2, 0x56, 0x9F, 0xEA, 0x7C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xF2, 0x00, 0xAF, 0xFA, 0x5C, 0xF2, 0x56, 0x9F, 0xEA, 0x7C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ // 60
+{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0x81, 0x00, 0x01, 0x1E, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
+{ 0x22, 0x10, 0x7E, 0xD8, 0x35, 0x2A, 0x2E, 0x8E, 0xD8, 0x7C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ // 70
+{ 0x22, 0x10, 0x7E, 0xD8, 0x35, 0x2A, 0x2E, 0x8E, 0xD8, 0x7C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE4, 0x08, 0x7E, 0x99, 0x28, 0xE6, 0x16, 0x80, 0xF8, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE4, 0x23, 0x8F, 0xF9, 0x7C, 0xE2, 0x18, 0x9F, 0x88, 0x7C, 0x01, 0x01, 0x04, 0x00, 0x1E, 0x00, 0x1F, 0x06, 0x05, 0x1F, 0x03, 0x29, 0x01, 0x1F, 0x02, 0x21, 0x01, 0x02, 0x1F, 0x00 },
+{ 0xE4, 0x23, 0x8F, 0xF9, 0x7C, 0xE2, 0x18, 0x9F, 0x88, 0x7C, 0x01, 0x01, 0x04, 0x00, 0x1E, 0x00, 0x1F, 0x06, 0x05, 0x1F, 0x03, 0x29, 0x01, 0x1F, 0x02, 0x21, 0x01, 0x02, 0x1F, 0x00 },
+{ 0xE4, 0x23, 0x8F, 0xF9, 0x7C, 0xE2, 0x18, 0x9F, 0x88, 0x7C, 0x01, 0x01, 0x04, 0x00, 0x1E, 0x00, 0x1F, 0x06, 0x05, 0x1F, 0x03, 0x29, 0x01, 0x1F, 0x02, 0x21, 0x01, 0x02, 0x1F, 0x00 },
+{ 0x2A, 0x1E, 0x98, 0xA9, 0x00, 0x62, 0x00, 0x9F, 0xB9, 0x7C, 0x00, 0x01, 0x00, 0x01, 0x1E, 0x00, 0x1F, 0x01, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x2A, 0x1E, 0x98, 0xA9, 0x00, 0x62, 0x00, 0x9F, 0xB9, 0x7C, 0x00, 0x01, 0x00, 0x01, 0x1E, 0x00, 0x1F, 0x01, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x62, 0xA8, 0x9D, 0x84, 0x44, 0x62, 0x23, 0x7F, 0xD5, 0x4C, 0x03, 0xA1, 0x00, 0x01, 0x1E, 0x02, 0x21, 0x01, 0x01, 0x20, 0xA8, 0x00, 0x01, 0x24, 0x06, 0x20, 0x04, 0x03, 0x24, 0x00 },
+{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ // 80
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xE4, 0x08, 0x7E, 0x99, 0x28, 0xE6, 0x16, 0x80, 0xF8, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x22, 0x10, 0x7E, 0xD8, 0x35, 0x2A, 0x2E, 0x8E, 0xD8, 0x7C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE4, 0x07, 0x05, 0xAA, 0x7C, 0xE2, 0x50, 0xBE, 0xC8, 0x7D, 0x07, 0x01, 0x00, 0x03, 0x1E, 0x01, 0x1E, 0x00, 0x00, 0x1E, 0xA8, 0x00, 0x01, 0x20, 0x06, 0x23, 0x04, 0x03, 0x20, 0x00 },
+{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ // 90
+{ 0xF2, 0x00, 0xAF, 0xFA, 0x5C, 0xF2, 0x56, 0x9F, 0xEA, 0x7C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0x81, 0x00, 0x01, 0x1E, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xE2, 0x00, 0xCE, 0xD9, 0x4C, 0xE2, 0x00, 0x8F, 0x99, 0x64, 0x0E, 0x81, 0x10, 0x00, 0x1E, 0x05, 0x1F, 0x12, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x1F, 0x06, 0x1E, 0x00, 0x06, 0x1F, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xC9, 0x40, 0x3A, 0x38, 0x5E, 0xC2, 0x00, 0x4C, 0xAA, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xC9, 0x40, 0x3A, 0x78, 0x5E, 0xC2, 0x00, 0x4D, 0x9A, 0x7C, 0x00, 0xA1, 0xA0, 0x08, 0x1F, 0x05, 0x1E, 0x02, 0x05, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+ // 100
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xC9, 0x40, 0x3A, 0x38, 0x5E, 0xC2, 0x00, 0x4C, 0xAA, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0xC9, 0x40, 0x3A, 0x78, 0x5E, 0xC2, 0x00, 0x4D, 0x9A, 0x7C, 0x00, 0xA1, 0xA0, 0x08, 0x1F, 0x05, 0x1E, 0x02, 0x05, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+ // 110
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x02, 0x0C, 0x03, 0x6A, 0x7D, 0x02, 0x00, 0x23, 0xEA, 0x7C, 0x02, 0x81, 0x00, 0x02, 0x20, 0x01, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xCF, 0x3B, 0x2A, 0xFE, 0x7E, 0xC0, 0xC0, 0x0C, 0xEB, 0x63, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 }, // Extrapolated
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x02, 0x0C, 0x03, 0x6A, 0x7D, 0x02, 0x00, 0x23, 0xEA, 0x7C, 0x02, 0x81, 0x00, 0x02, 0x20, 0x01, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x0F, 0x10, 0x10, 0x09, 0x49, 0x02, 0x12, 0x07, 0x9A, 0x7C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0xCF, 0x3B, 0x2A, 0xFE, 0x7E, 0xC0, 0xC0, 0x0C, 0xEB, 0x63, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+ // 120
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0xCF, 0x40, 0x0A, 0x30, 0x5C, 0xCF, 0x00, 0x0D, 0x80, 0x7C, 0x00, 0xA0, 0x00, 0x0F, 0x1E, 0x0F, 0x20, 0x00, 0x0B, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // Unknown
+};
+
+static byte lookup_table[64][32];
+const byte volume_table[] = {
+ 0, 4, 7, 11,
+ 13, 16, 18, 20,
+ 22, 24, 26, 27,
+ 29, 30, 31, 33,
+ 34, 35, 36, 37,
+ 38, 39, 40, 41,
+ 42, 43, 44, 44,
+ 45, 46, 47, 47,
+ 48, 49, 49, 50,
+ 51, 51, 52, 53,
+ 53, 54, 54, 55,
+ 55, 56, 56, 57,
+ 57, 58, 58, 59,
+ 59, 60, 60, 60,
+ 61, 61, 62, 62,
+ 62, 63, 63, 63
+};
+
+static int lookup_volume(int a, int b)
+{
+ if (b == 0)
+ return 0;
+
+ if (b == 31)
+ return a;
+
+ if (a < -63 || a > 63) {
+ return b * (a + 1) >> 5;
+ }
+
+ if (b < 0) {
+ if (a < 0) {
+ return lookup_table[-a][-b];
+ } else {
+ return -lookup_table[a][-b];
+ }
+ } else {
+ if (a < 0) {
+ return -lookup_table[-a][b];
+ } else {
+ return lookup_table[a][b];
+ }
+ }
+}
+
+static void create_lookup_table()
+{
+ int i, j;
+ int sum;
+
+ for (i = 0; i < 64; i++) {
+ sum = i;
+ for (j = 0; j < 32; j++) {
+ lookup_table[i][j] = sum >> 5;
+ sum += i;
+ }
+ }
+ for (i = 0; i < 64; i++)
+ lookup_table[i][0] = 0;
+}
+
+typedef void TimerCallback (void *);
+
+
+
+////////////////////////////////////////
+//
+// Adlib MIDI driver
+//
+////////////////////////////////////////
+
+class MidiDriver_ADLIB : public MidiDriver {
+public:
+ MidiDriver_ADLIB();
+
+ int open(int mode);
+ void close();
+ void send(uint32 b);
+ void pause(bool p) { }
+ void set_stream_callback(void *param, StreamCallback *sc) { } // No streaming support. Use MidiStreamer wrapper
+ void setPitchBendRange (byte channel, uint range);
+ void sysEx_customInstrument (byte channel, uint32 type, byte *instr);
+
+ void setTimerCallback (void *timer_param, void (*timer_proc) (void *));
+ uint32 getBaseTempo() {
+#ifdef _WIN32_WCE
+ return 0x1D9000 * 2; // Sampled down to 11 kHz
+#else //_WIN32_WCE
+ return 0x1D9000;
+#endif //_WIN32_WCE
+ }
+
+private:
+ int _mode;
+
+ FM_OPL *_opl;
+ byte *_adlib_reg_cache;
+ SoundMixer *_mixer;
+
+ TimerCallback *_timer_proc;
+ void *_timer_param;
+
+ int _adlib_timer_counter;
+
+ uint16 channel_table_2[9];
+ int _midichan_index;
+ int _next_tick;
+ uint16 curnote_table[9];
+ MidiChannelAdl _midi_channels[9];
+ AdlibPart _parts[16];
+
+ void generate_samples(int16 *buf, int len);
+ void on_timer();
+ void part_set_instrument (AdlibPart *part, Instrument * instr);
+ void part_key_on (byte chan, byte note, byte velocity);
+ void part_key_off (byte chan, byte note);
+
+ void adlib_key_off(int chan);
+ void adlib_note_on(int chan, byte note, int mod);
+ void adlib_note_on_ex(int chan, byte note, int mod);
+ int adlib_read_param(int chan, byte data);
+ void adlib_setup_channel(int chan, Instrument * instr, byte vol_1, byte vol_2);
+ byte adlib_read(byte port) {
+ return _adlib_reg_cache[port];
+ }
+ void adlib_set_param(int channel, byte param, int value);
+ void adlib_key_onoff(int channel);
+ void adlib_write(byte port, byte value);
+ void adlib_playnote(int channel, int note);
+
+ MidiChannelAdl *allocate_midichan(byte pri);
+
+ void reset_tick();
+ void mc_off(MidiChannelAdl * mc);
+
+ static void link_mc (AdlibPart *part, MidiChannelAdl *mc);
+ static void mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11);
+ static void mc_init_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11, byte flags,
+ InstrumentExtra * ie);
+
+ static void struct10_init(Struct10 * s10, InstrumentExtra * ie);
+ static byte struct10_ontimer(Struct10 * s10, Struct11 * s11);
+ static void struct10_setup(Struct10 * s10);
+ static int random_nr(int a);
+ void mc_key_on (MidiChannelAdl * mc, byte note, byte velocity);
+
+ static void premix_proc(void *param, int16 *buf, uint len);
+};
+
+
+
+// MidiDriver method implementations
+
+MidiDriver_ADLIB::MidiDriver_ADLIB()
+{
+ uint i;
+ for (i = 0; i < ARRAYSIZE(_parts); ++i) {
+ _parts [i]._chan = i;
+ _parts [i]._prev = (i ? &_parts[i-1] : 0);
+ _parts [i]._next = ((i < ARRAYSIZE(_parts) - 1) ? &_parts[i+1] : 0);
+ _parts [i]._drv = this;
+ }
+}
+
+int MidiDriver_ADLIB::open (int mode)
+{
+ if (_mode != 0)
+ return MERR_ALREADY_OPEN;
+ if (mode != MO_SIMPLE)
+ return MERR_STREAMING_NOT_AVAILABLE;
+ _mode = mode;
+
+ int i;
+ MidiChannelAdl *mc;
+
+ for (i = 0, mc = _midi_channels; i != ARRAYSIZE(_midi_channels); i++, mc++) {
+ mc->_channel = i;
+ mc->_s11a.s10 = &mc->_s10b;
+ mc->_s11b.s10 = &mc->_s10a;
+ }
+
+ _adlib_reg_cache = (byte *)calloc(256, 1);
+
+ _opl = OPLCreate(OPL_TYPE_YM3812, 3579545, g_system->property(OSystem::PROP_GET_SAMPLE_RATE, 0));
+
+ adlib_write(1, 0x20);
+ adlib_write(8, 0x40);
+ adlib_write(0xBD, 0x00);
+ create_lookup_table();
+
+ _mixer = g_mixer;
+ _mixer->setupPremix(this, premix_proc);
+ return 0;
+}
+
+void MidiDriver_ADLIB::close()
+{
+ int i;
+ for (i = 0; i < ARRAYSIZE(_midi_channels); ++i) {
+ if (_midi_channels [i]._part)
+ mc_off (&_midi_channels [i]);
+ }
+
+ // Detach the premix callback handler
+ _mixer->setupPremix (0, 0);
+}
+
+void MidiDriver_ADLIB::send (uint32 b)
+{
+ if (_mode != MO_SIMPLE)
+ error("MidiDriver_ADLIB::send called but driver is not in simple mode");
+
+ MidiChannelAdl *mc;
+
+ byte param3 = (byte) ((b >> 24) & 0xFF);
+ byte param2 = (byte) ((b >> 16) & 0xFF);
+ byte param1 = (byte) ((b >> 8) & 0xFF);
+ byte cmd = (byte) (b & 0xF0);
+ byte chan = (byte) (b & 0x0F);
+ AdlibPart *part = &_parts [chan];
+
+ switch (cmd) {
+ case 0x80:// Note Off
+ part_key_off (chan, param1);
+ break;
+
+ case 0x90: // Note On
+ part_key_on (chan, param1, param2);
+ break;
+
+ case 0xA0: // Aftertouch
+ // Not supported.
+ break;
+
+ case 0xB0: // Control Change
+ switch (param1) {
+ case 01: // Modulation wheel
+ part->_modwheel = param2;
+ for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
+ if (mc->_s10a.active && mc->_s11a.flag0x40)
+ mc->_s10a.modwheel = part->_modwheel >> 2;
+ if (mc->_s10b.active && mc->_s11b.flag0x40)
+ mc->_s10b.modwheel = part->_modwheel >> 2;
+ }
+ break;
+
+ case 07: // Volume
+ part->_vol_eff = param2;
+ for (mc = part->_mc; mc; mc = mc->_next) {
+ adlib_set_param(mc->_channel, 0, volume_table[lookup_table[mc->_vol_2][part->_vol_eff >> 2]]);
+ if (mc->_twochan) {
+ adlib_set_param(mc->_channel, 13, volume_table[lookup_table[mc->_vol_1][part->_vol_eff >> 2]]);
+ }
+ }
+ break;
+
+ case 10: // Pan position
+ // Not supported in Adlib (OPL2)
+ break;
+
+ case 16: // Pitchbend factor
+ part->_pitchbend_factor = param2;
+ for (mc = part->_mc; mc; mc = mc->_next) {
+ adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
+ (part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
+ }
+ break;
+
+ case 17: // GP slider 2 (detune)
+ part->_detune_eff = param2;
+ for (mc = part->_mc; mc; mc = mc->_next) {
+ adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
+ (part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
+ }
+ break;
+
+ case 18: // GP slider 3 (priority)
+ part->_pri_eff = param2;
+ break;
+
+ case 64: // Sustain pedal
+ part->_pedal = (param2 > 0);
+ if (!part->_pedal) {
+ for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
+ if (mc->_waitforpedal)
+ mc_off(mc);
+ }
+ }
+ break;
+
+ case 91: // Effects level
+ // Not supported in Adlib (OPL2)
+ break;
+
+ case 93: // Chorus
+ // Not supported in Adlib (OPL2)
+ break;
+
+ case 123: // All Notes Off
+ while (part->_mc)
+ mc_off (part->_mc);
+ break;
+
+ default:
+ warning ("MidiDriver_ADLIB: Unknown control change message %d", param1);
+ }
+ break;
+
+ case 0xC0: // Program Change
+ if (chan != 9) {
+ if (!map_gm_to_fm [part->_program][0])
+ warning ("No Adlib instrument defined for GM program %d", (int) param1);
+ part->_program = param1;
+ part_set_instrument (&_parts[chan], (Instrument *) &map_gm_to_fm[part->_program]);
+ }
+ break;
+
+ case 0xD0: // Channel Pressure
+ // Not supported.
+ break;
+
+ case 0xE0: // Pitch Bend
+ part->_pitchbend = (param1 | (param2 << 7)) - 0x2000;
+ for (mc = part->_mc; mc; mc = mc->_next) {
+ adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
+ (part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
+ }
+ break;
+
+ case 0xF0: // SysEx
+ // We should never get here! SysEx information has to be
+ // sent via high-level semantic methods.
+ warning ("MidiDriver_ADLIB: Receiving SysEx command on a send() call");
+ break;
+
+ default:
+ warning ("MidiDriver_ADLIB: Unknown send() command 0x%02X", cmd);
+ }
+}
+
+void MidiDriver_ADLIB::setPitchBendRange (byte channel, uint range)
+{
+ MidiChannelAdl *mc;
+ AdlibPart *part = &_parts [channel];
+
+ part->_pitchbend_factor = range;
+ for (mc = part->_mc; mc; mc = mc->_next) {
+ adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
+ (part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
+ }
+}
+
+void MidiDriver_ADLIB::sysEx_customInstrument (byte channel, uint32 type, byte *instr)
+{
+ if (type == 'ADL ') {
+ Instrument *i = &_parts[channel]._part_instr;
+ memcpy(i, instr, sizeof(Instrument));
+ }
+}
+
+void MidiDriver_ADLIB::setTimerCallback (void *timer_param, void (*timer_proc) (void *))
+{
+ _timer_proc = (TimerCallback *) timer_proc;
+ _timer_param = timer_param;
+}
+
+MidiDriver *MidiDriver_ADLIB_create()
+{
+ return new MidiDriver_ADLIB();
+}
+
+
+
+// All the code brought over from IMuseAdlib
+
+void MidiDriver_ADLIB::premix_proc(void *param, int16 *buf, uint len)
+{
+ ((MidiDriver_ADLIB *) param)->generate_samples(buf, len);
+}
+
+void MidiDriver_ADLIB::adlib_write(byte port, byte value)
+{
+ if (_adlib_reg_cache[port] == value)
+ return;
+ _adlib_reg_cache[port] = value;
+
+ OPLWriteReg(_opl, port, value);
+}
+
+void MidiDriver_ADLIB::generate_samples(int16 *data, int len)
+{
+ int step;
+
+ if (!_opl) {
+ memset(data, 0, len * sizeof(int16));
+ return;
+ }
+
+ do {
+ step = len;
+ if (step > _next_tick)
+ step = _next_tick;
+ YM3812UpdateOne(_opl, data, step);
+
+ if (!(_next_tick -= step)) {
+ if (_timer_proc)
+ (*_timer_proc) (_timer_param);
+ on_timer();
+ reset_tick();
+ }
+ data += step;
+ } while (len -= step);
+}
+
+void MidiDriver_ADLIB::reset_tick()
+{
+ _next_tick = 88;
+}
+
+void MidiDriver_ADLIB::on_timer()
+{
+ MidiChannelAdl *mc;
+ int i;
+
+ _adlib_timer_counter += 0xD69;
+ while (_adlib_timer_counter >= 0x411B) {
+ _adlib_timer_counter -= 0x411B;
+ mc = _midi_channels;
+ for (i = 0; i != ARRAYSIZE(_midi_channels); i++, mc++) {
+ if (!mc->_part)
+ continue;
+ if (mc->_duration && (mc->_duration -= 0x11) <= 0) {
+ mc_off(mc);
+ return;
+ }
+ if (mc->_s10a.active) {
+ mc_inc_stuff(mc, &mc->_s10a, &mc->_s11a);
+ }
+ if (mc->_s10b.active) {
+ mc_inc_stuff(mc, &mc->_s10b, &mc->_s11b);
+ }
+ }
+ }
+}
+
+void MidiDriver_ADLIB::mc_off(MidiChannelAdl * mc2)
+{
+ MidiChannelAdl *mc = (MidiChannelAdl *)mc2, *tmp;
+
+ adlib_key_off(mc->_channel);
+
+ tmp = mc->_prev;
+
+ if (mc->_next)
+ mc->_next->_prev = tmp;
+ if (tmp)
+ tmp->_next = mc->_next;
+ else
+ mc->_part->_mc = mc->_next;
+ mc->_part = NULL;
+}
+
+void MidiDriver_ADLIB::mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11)
+{
+ byte code;
+ AdlibPart *part = mc->_part;
+
+ code = struct10_ontimer(s10, s11);
+
+ if (code & 1) {
+ switch (s11->param) {
+ case 0:
+ mc->_vol_2 = s10->start_value + s11->modify_val;
+ ((MidiDriver_ADLIB *) part->_drv)->adlib_set_param(mc->_channel, 0,
+ volume_table[lookup_table[mc->_vol_2]
+ [part->_vol_eff >> 2]]);
+ break;
+ case 13:
+ mc->_vol_1 = s10->start_value + s11->modify_val;
+ if (mc->_twochan) {
+ ((MidiDriver_ADLIB *) part->_drv)->adlib_set_param(mc->_channel, 13,
+ volume_table[lookup_table[mc->_vol_1]
+ [part->_vol_eff >> 2]]);
+ } else {
+ ((MidiDriver_ADLIB *) part->_drv)->adlib_set_param(mc->_channel, 13, mc->_vol_1);
+ }
+ break;
+ case 30:
+ s11->s10->modwheel = (char)s11->modify_val;
+ break;
+ case 31:
+ s11->s10->unk3 = (char)s11->modify_val;
+ break;
+ default:
+ ((MidiDriver_ADLIB *) part->_drv)->adlib_set_param(mc->_channel, s11->param,
+ s10->start_value + s11->modify_val);
+ break;
+ }
+ }
+
+ if (code & 2 && s11->flag0x10)
+ ((MidiDriver_ADLIB *) part->_drv)->adlib_key_onoff(mc->_channel);
+}
+
+void MidiDriver_ADLIB::adlib_key_off(int chan)
+{
+ byte port = chan + 0xB0;
+ adlib_write(port, adlib_read(port) & ~0x20);
+}
+
+byte MidiDriver_ADLIB::struct10_ontimer(Struct10 * s10, Struct11 * s11)
+{
+ byte result = 0;
+ int i;
+
+ if (s10->count && (s10->count -= 17) <= 0) {
+ s10->active = 0;
+ return 0;
+ }
+
+ i = s10->cur_val + s10->speed_hi;
+ s10->speed_lo_counter += s10->speed_lo;
+ if (s10->speed_lo_counter >= s10->speed_lo_max) {
+ s10->speed_lo_counter -= s10->speed_lo_max;
+ i += s10->direction;
+ }
+ if (s10->cur_val != i || s10->modwheel != s10->modwheel_last) {
+ s10->cur_val = i;
+ s10->modwheel_last = s10->modwheel;
+ i = lookup_volume(i, s10->modwheel_last);
+ if (i != s11->modify_val) {
+ s11->modify_val = i;
+ result = 1;
+ }
+ }
+
+ if (!--s10->num_steps) {
+ s10->active++;
+ if (s10->active > 4) {
+ if (s10->loop) {
+ s10->active = 1;
+ result |= 2;
+ struct10_setup(s10);
+ } else {
+ s10->active = 0;
+ }
+ } else {
+ struct10_setup(s10);
+ }
+ }
+
+ return result;
+}
+
+void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value)
+{
+ const AdlibSetParams *as;
+ byte port;
+
+ assert(channel >= 0 && channel < 9);
+
+ if (param <= 12) {
+ port = channel_mappings_2[channel];
+ } else if (param <= 25) {
+ param -= 13;
+ port = channel_mappings[channel];
+ } else if (param <= 27) {
+ param -= 13;
+ port = channel;
+ } else if (param == 28 || param == 29) {
+ if (param == 28)
+ value -= 15;
+ else
+ value -= 383;
+ value <<= 4;
+ channel_table_2[channel] = value;
+ adlib_playnote(channel, curnote_table[channel] + value);
+ return;
+ } else {
+ return;
+ }
+
+ as = &adlib_setparam_table[param];
+ if (as->d)
+ value = as->d - value;
+ port += as->a;
+ adlib_write(port, (adlib_read(port) & ~as->c) | (((byte)value) << as->b));
+}
+
+void MidiDriver_ADLIB::adlib_key_onoff(int channel)
+{
+ byte val;
+ byte port = channel + 0xB0;
+ assert(channel >= 0 && channel < 9);
+
+ val = adlib_read(port);
+ adlib_write(port, val & ~0x20);
+ adlib_write(port, val | 0x20);
+}
+
+void MidiDriver_ADLIB::struct10_setup(Struct10 * s10)
+{
+ int b, c, d, e, f, g, h;
+ byte t;
+
+ b = s10->unk3;
+ f = s10->active - 1;
+
+ t = s10->table_a[f];
+ e = num_steps_table[lookup_table[t & 0x7F][b]];
+ if (t & 0x80) {
+ e = random_nr(e);
+ }
+ if (e == 0)
+ e++;
+
+ s10->num_steps = s10->speed_lo_max = e;
+
+ if (f != 2) {
+ c = s10->param;
+ g = s10->start_value;
+ t = s10->table_b[f];
+ d = lookup_volume(c, (t & 0x7F) - 31);
+ if (t & 0x80) {
+ d = random_nr(d);
+ }
+ if (d + g > c) {
+ h = c - g;
+ } else {
+ h = d;
+ if (d + g < 0)
+ h = -g;
+ }
+ h -= s10->cur_val;
+ } else {
+ h = 0;
+ }
+
+ s10->speed_hi = h / e;
+ if (h < 0) {
+ h = -h;
+ s10->direction = -1;
+ } else {
+ s10->direction = 1;
+ }
+
+ s10->speed_lo = h % e;
+ s10->speed_lo_counter = 0;
+}
+
+void MidiDriver_ADLIB::adlib_playnote(int channel, int note)
+{
+ byte old, oct, notex;
+ int note2;
+ int i;
+
+ note2 = (note >> 7) - 4;
+
+ oct = octave_numbers[note2] << 2;
+ notex = note_numbers[note2];
+
+ old = adlib_read(channel + 0xB0);
+ if (old & 0x20) {
+ old &= ~0x20;
+ if (oct > old) {
+ if (notex < 6) {
+ notex += 12;
+ oct -= 4;
+ }
+ } else if (oct < old) {
+ if (notex > 11) {
+ notex -= 12;
+ oct += 4;
+ }
+ }
+ }
+
+ i = (notex << 3) + ((note >> 4) & 0x7);
+ adlib_write(channel + 0xA0, note_to_f_num[i]);
+ adlib_write(channel + 0xB0, oct | 0x20);
+}
+
+int MidiDriver_ADLIB::random_nr(int a)
+{
+ static byte _rand_seed = 1;
+ if (_rand_seed & 1) {
+ _rand_seed >>= 1;
+ _rand_seed ^= 0xB8;
+ } else {
+ _rand_seed >>= 1;
+ }
+ return _rand_seed * a >> 8;
+}
+
+void MidiDriver_ADLIB::part_key_off (byte chan, byte note)
+{
+ MidiChannelAdl *mc;
+
+ // Percussion is not implemented; filter that channel
+ if (chan == 9)
+ return;
+
+ AdlibPart *part;
+ for (part = _parts; part; part = part->_next) {
+ if (part->_chan == chan)
+ break;
+ }
+ if (!part)
+ return;
+
+ for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
+ if (mc->_note == note) {
+ if (part->_pedal)
+ mc->_waitforpedal = true;
+ else
+ mc_off(mc);
+ }
+ }
+}
+
+void MidiDriver_ADLIB::part_key_on (byte chan, byte note, byte velocity)
+{
+ MidiChannelAdl *mc;
+
+ // Percussion is not implemented; filter that channel
+ if (chan == 9)
+ return;
+
+ AdlibPart *part;
+ for (part = _parts; part; part = part->_next) {
+ if (part->_chan == chan)
+ break;
+ }
+ if (!part)
+ return;
+
+ mc = allocate_midichan(part->_pri_eff);
+ if (!mc)
+ return;
+
+ link_mc(part, mc);
+ mc_key_on(mc, note, velocity);
+}
+
+MidiChannelAdl *MidiDriver_ADLIB::allocate_midichan(byte pri)
+{
+ MidiChannelAdl *ac, *best = NULL;
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ if (++_midichan_index >= 9)
+ _midichan_index = 0;
+ ac = &_midi_channels[_midichan_index];
+ if (!ac->_part)
+ return ac;
+ if (!ac->_next) {
+ if (ac->_part->_pri_eff <= pri) {
+ pri = ac->_part->_pri_eff;
+ best = ac;
+ }
+ }
+ }
+
+ if (best)
+ mc_off(best);
+ return best;
+}
+
+void MidiDriver_ADLIB::link_mc (AdlibPart *part, MidiChannelAdl *mc)
+{
+ mc->_part = part;
+ mc->_next = (MidiChannelAdl *)part->_mc;
+ part->_mc = mc;
+ mc->_prev = NULL;
+
+ if (mc->_next)
+ mc->_next->_prev = mc;
+}
+
+void MidiDriver_ADLIB::mc_key_on (MidiChannelAdl * mc2, byte note, byte velocity)
+{
+ MidiChannelAdl *mc = (MidiChannelAdl *)mc2;
+ AdlibPart *part = mc->_part;
+ Instrument *instr = &part->_part_instr;
+ int c;
+ byte vol_1, vol_2;
+
+ mc->_twochan = instr->feedback & 1;
+ mc->_note = note;
+ mc->_waitforpedal = false;
+ mc->_duration = instr->duration;
+ if (mc->_duration != 0)
+ mc->_duration *= 63;
+
+ vol_1 = (instr->oplvl_1 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_1 >> 2];
+ if (vol_1 > 0x3F)
+ vol_1 = 0x3F;
+ mc->_vol_1 = vol_1;
+
+ vol_2 = (instr->oplvl_2 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_2 >> 2];
+ if (vol_2 > 0x3F)
+ vol_2 = 0x3F;
+ mc->_vol_2 = vol_2;
+
+ c = part->_vol_eff >> 2;
+
+ vol_2 = volume_table[lookup_table[vol_2][c]];
+ if (mc->_twochan)
+ vol_1 = volume_table[lookup_table[vol_1][c]];
+
+ adlib_setup_channel(mc->_channel, instr, vol_1, vol_2);
+ adlib_note_on_ex(mc->_channel, part->_transpose_eff + note, part->_detune_eff + (part->_pitchbend * part->_pitchbend_factor >> 6));
+
+ if (instr->flags_a & 0x80) {
+ mc_init_stuff(mc, &mc->_s10a, &mc->_s11a, instr->flags_a, &instr->extra_a);
+ } else {
+ mc->_s10a.active = 0;
+ }
+
+ if (instr->flags_b & 0x80) {
+ mc_init_stuff(mc, &mc->_s10b, &mc->_s11b, instr->flags_b, &instr->extra_b);
+ } else {
+ mc->_s10b.active = 0;
+ }
+}
+
+void MidiDriver_ADLIB::adlib_setup_channel(int chan, Instrument * instr, byte vol_1, byte vol_2)
+{
+ byte port;
+
+ assert(chan >= 0 && chan < 9);
+
+ port = channel_mappings[chan];
+ adlib_write(port + 0x20, instr->flags_1);
+
+ // FIXME: Without using g_scumm, this may make older games sound weird.
+ // Gotta transform the volume *before* it gets sent here.
+ if (/*!(g_scumm->_features & GF_SMALL_HEADER)*/true ||(instr->feedback & 1))
+ adlib_write(port + 0x40, (instr->oplvl_1 | 0x3F) - vol_1 );
+ else
+ adlib_write(port + 0x40, instr->oplvl_1);
+
+ adlib_write(port + 0x60, 0xff & (~instr->atdec_1));
+ adlib_write(port + 0x80, 0xff & (~instr->sustrel_1));
+ adlib_write(port + 0xE0, instr->waveform_1);
+
+ port = channel_mappings_2[chan];
+ adlib_write(port + 0x20, instr->flags_2);
+ adlib_write(port + 0x40, (instr->oplvl_2 | 0x3F) - vol_2 );
+ adlib_write(port + 0x60, 0xff & (~instr->atdec_2));
+ adlib_write(port + 0x80, 0xff & (~instr->sustrel_2));
+ adlib_write(port + 0xE0, instr->waveform_2);
+
+ adlib_write((byte)chan + 0xC0, instr->feedback);
+}
+
+void MidiDriver_ADLIB::adlib_note_on_ex(int chan, byte note, int mod)
+{
+ int code;
+ assert(chan >= 0 && chan < 9);
+ code = (note << 7) + mod;
+ curnote_table[chan] = code;
+ channel_table_2[chan] = 0;
+ adlib_playnote(chan, code);
+}
+
+void MidiDriver_ADLIB::mc_init_stuff (MidiChannelAdl *mc, Struct10 * s10,
+ Struct11 * s11, byte flags, InstrumentExtra * ie)
+{
+ AdlibPart *part = mc->_part;
+
+ s11->modify_val = 0;
+ s11->flag0x40 = flags & 0x40;
+ s10->loop = flags & 0x20;
+ s11->flag0x10 = flags & 0x10;
+ s11->param = param_table_1[flags & 0xF];
+ s10->param = param_table_2[flags & 0xF];
+ s10->unk3 = 31;
+ if (s11->flag0x40) {
+ s10->modwheel = part->_modwheel >> 2;
+ } else {
+ s10->modwheel = 31;
+ }
+
+ switch (s11->param) {
+ case 0:
+ s10->start_value = mc->_vol_2;
+ break;
+ case 13:
+ s10->start_value = mc->_vol_1;
+ break;
+ case 30:
+ s10->start_value = 31;
+ s11->s10->modwheel = 0;
+ break;
+ case 31:
+ s10->start_value = 0;
+ s11->s10->unk3 = 0;
+ break;
+ default:
+ s10->start_value = ((MidiDriver_ADLIB *) part->_drv)->adlib_read_param(mc->_channel, s11->param);
+ }
+
+ struct10_init(s10, ie);
+}
+
+void MidiDriver_ADLIB::struct10_init(Struct10 * s10, InstrumentExtra * ie)
+{
+ s10->active = 1;
+ s10->cur_val = 0;
+ s10->modwheel_last = 31;
+ s10->count = ie->a;
+ if (s10->count)
+ s10->count *= 63;
+ s10->table_a[0] = ie->b;
+ s10->table_a[1] = ie->d;
+ s10->table_a[2] = ie->f;
+ s10->table_a[3] = ie->g;
+
+ s10->table_b[0] = ie->c;
+ s10->table_b[1] = ie->e;
+ s10->table_b[2] = 0;
+ s10->table_b[3] = ie->h;
+
+ struct10_setup(s10);
+}
+
+int MidiDriver_ADLIB::adlib_read_param(int chan, byte param)
+{
+ const AdlibSetParams *as;
+ byte val;
+ byte port;
+
+ assert(chan >= 0 && chan < 9);
+
+ if (param <= 12) {
+ port = channel_mappings_2[chan];
+ } else if (param <= 25) {
+ param -= 13;
+ port = channel_mappings[chan];
+ } else if (param <= 27) {
+ param -= 13;
+ port = chan;
+ } else if (param == 28) {
+ return 0xF;
+ } else if (param == 29) {
+ return 0x17F;
+ } else {
+ return 0;
+ }
+
+ as = &adlib_setparam_table[param];
+ val = adlib_read(port + as->a);
+ val &= as->c;
+ val >>= as->b;
+ if (as->d)
+ val = as->d - val;
+
+ return val;
+}
+
+void MidiDriver_ADLIB::adlib_note_on(int chan, byte note, int mod)
+{
+ int code;
+ assert(chan >= 0 && chan < 9);
+ code = (note << 7) + mod;
+ curnote_table[chan] = code;
+ adlib_playnote(chan, channel_table_2[chan] + code);
+}
+
+void MidiDriver_ADLIB::part_set_instrument(AdlibPart *part, Instrument * instr)
+{
+ Instrument *i = &part->_part_instr;
+ memcpy(i, instr, sizeof(Instrument));
+}
diff --git a/common/gameDetector.cpp b/common/gameDetector.cpp
index 9e766674f2..9195ef2c67 100644
--- a/common/gameDetector.cpp
+++ b/common/gameDetector.cpp
@@ -408,7 +408,7 @@ bool GameDetector::parseMusicDriver(const char *s) {
{"etude",MD_ETUDE},
{"midiemu",MD_MIDIEMU},
{"alsa", MD_ALSA},
- {"adlib",-1},
+ {"adlib", MD_ADLIB},
};
const MusicDrivers *md = music_drivers;
@@ -418,7 +418,7 @@ bool GameDetector::parseMusicDriver(const char *s) {
for(i=0; i!=ARRAYSIZE(music_drivers); i++,md++) {
if (!scumm_stricmp(md->name, s)) {
- if (md->id == -1) {
+ if (md->id == MD_ADLIB) {
_use_adlib = true;
}
_midi_driver = md->id;
@@ -574,6 +574,7 @@ int GameDetector::detectMain()
* and the game is one of those that want adlib as
* default */
if (_midi_driver == MD_AUTO && _features & GF_ADLIB_DEFAULT) {
+ _midi_driver = MD_ADLIB;
_use_adlib = true;
}
@@ -649,15 +650,19 @@ MidiDriver *GameDetector::createMidi() {
/* FIXME: We should, for the Unix targets, attempt to detect */
/* whether a sequencer is available, and use it in */
/* preference */
+/*
if (drv == MD_AUTO) {
_use_adlib = true;
return NULL;
}
+*/
+ if (drv == MD_AUTO) drv = MD_ADLIB;
#endif
switch(drv) {
case MD_AUTO:
case MD_NULL: return MidiDriver_NULL_create();
+ case MD_ADLIB: _use_adlib = true; return MidiDriver_ADLIB_create();
#if defined(WIN32) && !defined(_WIN32_WCE)
case MD_WINDOWS: return MidiDriver_WIN_create();
#endif
diff --git a/scumm/imuse.cpp b/scumm/imuse.cpp
index 73e9d514fd..ed8ef82c6c 100644
--- a/scumm/imuse.cpp
+++ b/scumm/imuse.cpp
@@ -355,7 +355,8 @@ public:
pcProgram = 64,
pcChorus = 128,
pcPitchBendFactor = 256,
- pcAll = 511,
+ pcPriority = 512,
+ pcAll = 1023,
};
virtual void on_timer() = 0;
@@ -665,6 +666,8 @@ class IMuseGM : public IMuseDriver {
MidiDriver *_md;
MidiChannelGM _midi_channels[16];
+ Instrument _glob_instr[32]; // Adlib custom instruments
+
byte _midi_program_last[16];
int16 _midi_pitchbend_last[16];
byte _midi_pitchbend_factor_last[16];
@@ -691,6 +694,8 @@ class IMuseGM : public IMuseDriver {
void midiSilence(byte chan);
void midiInit();
+ static void timer_callback (void *);
+
public:
IMuseGM(MidiDriver *midi);
void uninit();
@@ -700,8 +705,8 @@ public:
int part_update_active(Part *part, uint16 *active);
void on_timer() {}
- void set_instrument(uint slot, byte *instr) {}
- void part_set_instrument(Part *part, Instrument * instr) {}
+ void set_instrument(uint slot, byte *instr);
+ void part_set_instrument(Part *part, Instrument * instr);
void part_set_param(Part *part, byte param, int value) {}
void part_key_on(Part *part, byte note, byte velocity);
void part_key_off(Part *part, byte note);
@@ -710,7 +715,7 @@ public:
static int midi_driver_thread(void *param);
- uint32 get_base_tempo() { return 0x4A0000; }
+ uint32 get_base_tempo() { return _md->getBaseTempo(); } // 0x4A0000; }
byte get_hardware_type() { return 5; }
};
@@ -2199,7 +2204,6 @@ byte *Player::parse_midi(byte *s)
case 0xC: /* program change */
value = *s++;
- debug (2, "Player::parse_midi - Setting channel %2d to program %d", chan, value); // Jamieson630: Helps to build the GM-to-FM mapping
part = get_part(chan);
if (part)
part->set_program(value);
@@ -2212,7 +2216,6 @@ byte *Player::parse_midi(byte *s)
case 0xE: /* pitch bend */
part = get_part(chan);
if (part)
- // part->set_pitchbend(((s[1] - 0x40) << 7) | s[0]);
part->set_pitchbend(((s[1] << 7) | s[0]) - 0x2000);
s += 2;
break;
@@ -2392,7 +2395,7 @@ void Player::parse_sysex(byte *p, uint len)
if (part) {
part->set_onoff (p[2] & 0x01);
part->set_vol ((p[5] & 0x0F) << 4 | (p[6] & 0x0F));
- part->_percussion = ((p[9] & 0x08) > 0);
+ part->_percussion = _isGM ? ((p[9] & 0x08) > 0) : false;
if (part->_percussion) {
if (part->_mc)
part->off();
@@ -2431,7 +2434,7 @@ void Player::parse_sysex(byte *p, uint len)
case 16: /* set instrument in part */
a = *p++ & 0x0F;
- if (_se->_hardware_type != *p++)
+ if (_se->_hardware_type != *p++ && false)
break;
decode_sysex_bytes(p, buf, len - 3);
part = get_part(a);
@@ -2441,7 +2444,7 @@ void Player::parse_sysex(byte *p, uint len)
case 17: /* set global instrument */
p++;
- if (_se->_hardware_type != *p++)
+ if (_se->_hardware_type != *p++ && false)
break;
a = *p++;
decode_sysex_bytes(p, buf, len - 4);
@@ -3422,6 +3425,7 @@ void Part::set_vol(uint8 vol)
void Part::set_pri(int8 pri)
{
_pri_eff = clamp((_pri = pri) + _player->_priority, 0, 255);
+ changed(IMuseDriver::pcPriority);
}
void Part::set_pan(int8 pan)
@@ -4947,7 +4951,8 @@ void IMuseGM::init(IMuseInternal *eng, OSystem *syst)
/* Install the on_timer thread */
_se = eng;
- syst->create_thread(midi_driver_thread, this);
+// syst->create_thread(midi_driver_thread, this);
+ _md->setTimerCallback (NULL, &IMuseGM::timer_callback);
for (i = 0, mc = _midi_channels; i != ARRAYSIZE(_midi_channels); i++, mc++) {
mc->_chan = i;
@@ -4955,6 +4960,10 @@ void IMuseGM::init(IMuseInternal *eng, OSystem *syst)
}
}
+void IMuseGM::timer_callback (void *) {
+ g_scumm->_imuse->on_timer();
+}
+
void IMuseGM::uninit()
{
_md->close();
@@ -5034,6 +5043,21 @@ int IMuseGM::part_update_active(Part *part, uint16 *active)
return count;
}
+void IMuseGM::part_set_instrument (Part *part, Instrument *instr)
+{
+ if (!part->_mc)
+ update_pris();
+ if (!part->_mc)
+ return;
+ _md->sysEx_customInstrument (part->_mc->gm()->_chan, 'ADL ', (byte *)instr);
+}
+
+void IMuseGM::set_instrument(uint slot, byte *data)
+{
+ if (slot < 32)
+ memcpy(&_glob_instr[slot], data, sizeof(Instrument));
+}
+
void IMuseGM::part_changed(Part *part, uint16 what)
{
MidiChannelGM *mc;
@@ -5078,18 +5102,25 @@ void IMuseGM::part_changed(Part *part, uint16 what)
midiEffectLevel(mc->_chan, part->_effect_level);
if (what & pcProgram && part->_program < 128) {
- _midi_program_last [part->_chan] = part->_program;
- if (part->_bank) {
- midiControl0(mc->_chan, part->_bank);
- midiProgram(mc->_chan, part->_program, part->_player->_mt32emulate);
- midiControl0(mc->_chan, 0);
- } else {
- midiProgram(mc->_chan, part->_program, part->_player->_mt32emulate);
+ if (part->_player->_isGM) {
+ _midi_program_last [part->_chan] = part->_program;
+ if (part->_bank) {
+ midiControl0(mc->_chan, part->_bank);
+ midiProgram(mc->_chan, part->_program, part->_player->_mt32emulate);
+ midiControl0(mc->_chan, 0);
+ } else {
+ midiProgram(mc->_chan, part->_program, part->_player->_mt32emulate);
+ }
+ } else if (part->_program < 32) {
+ part_set_instrument(part, &_glob_instr[part->_program]);
}
}
if (what & pcChorus)
midiChorus(mc->_chan, part->_effect_level);
+
+ if (what & pcPriority)
+ _md->send ((part->_pri_eff << 16) | (18 << 8) | 0xB0 | mc->_chan);
}
diff --git a/scumm/resource.cpp b/scumm/resource.cpp
index 4a4a01f189..0caadf6bf0 100644
--- a/scumm/resource.cpp
+++ b/scumm/resource.cpp
@@ -608,20 +608,21 @@ int Scumm::readSoundResource(int type, int idx)
pri = 15;
break;
case MKID('ADL '):
+ pri = 1;
if (_use_adlib)
pri = 10;
break;
case MKID('ROL '):
- if (!_use_adlib)
- pri = 2;
+// if (!_use_adlib)
+ pri = 3;
break;
case MKID('GMD '):
// if (!_use_adlib)
- pri = 3;
+ pri = 4;
break;
case MKID('MAC '):
// if (!_use_adlib)
- pri = 1;
+ pri = 2;
break;
case MKID('SPK '):
if (!_use_adlib)
diff --git a/scumm/scummvm.cpp b/scumm/scummvm.cpp
index ba61000857..06579fbbce 100644
--- a/scumm/scummvm.cpp
+++ b/scumm/scummvm.cpp
@@ -165,11 +165,11 @@ Scumm::Scumm (GameDetector *detector, OSystem *syst)
_imuseDigital = new IMuseDigital(this);
_imuse = NULL;
} else {
- if (detector->_use_adlib) {
+ if (detector->_use_adlib && false) {
_imuse = IMuse::create_adlib(syst, _mixer);
} else {
void *midiTemp = detector->createMidi();
- if (!midiTemp) { // Fallback to adlib/midiemu
+ if (!midiTemp) { // Fallback to Adlib
_imuse = IMuse::create_adlib(syst, _mixer);
} else {
_imuse = IMuse::create_midi(syst, detector->createMidi());
diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp
index 9b9b8fe416..baf3427cfb 100644
--- a/sound/mididrv.cpp
+++ b/sound/mididrv.cpp
@@ -35,6 +35,67 @@
#include "common/engine.h" // for warning/error/debug
#include "common/util.h"
+
+
+////////////////////////////////////////
+//
+// Common MPU401 implementation methods
+//
+////////////////////////////////////////
+
+typedef void TimerCallback (void *);
+
+class MidiDriver_MPU401 : public MidiDriver {
+private:
+ bool _started_thread;
+ TimerCallback *_timer_proc;
+ void *_timer_param;
+
+ static int midi_driver_thread (void *param);
+
+public:
+ virtual void setTimerCallback (void *timer_param, void (*timer_proc) (void *));
+ virtual uint32 getBaseTempo (void) { return 0x4A0000; }
+};
+
+void MidiDriver_MPU401::setTimerCallback (void *timer_param, void (*timer_proc) (void *))
+{
+ if (!_timer_proc || !timer_proc) {
+ _timer_proc = (TimerCallback *) timer_proc;
+ _timer_param = timer_param;
+ if (!_started_thread && timer_proc)
+ g_system->create_thread (midi_driver_thread, this);
+ _started_thread = true;
+ }
+}
+
+int MidiDriver_MPU401::midi_driver_thread(void *param)
+{
+ MidiDriver_MPU401 *mid = (MidiDriver_MPU401 *)param;
+ int old_time, cur_time;
+
+ old_time = g_system->get_msecs();
+
+ for (;;) {
+ g_system->delay_msecs(10);
+
+ cur_time = g_system->get_msecs();
+ while (old_time < cur_time) {
+ old_time += 10;
+ // Don't use mid->_se_on_timer()
+ // We must come in through IMuseMonitor to protect
+ // against conflicts with script access to IMuse.
+ if (mid->_timer_proc)
+ (*(mid->_timer_proc)) (mid->_timer_param);
+ }
+ }
+
+ return 0;
+}
+
+
+
+
// FIXME - the following disables reverb support in the QuickTime / CoreAudio
// midi backends. For some reasons, reverb will suck away a *lot* of CPU time.
// Until we know for sure what is causing this and if there is a better way to
@@ -44,7 +105,7 @@
#if defined(WIN32) && !defined(_WIN32_WCE)
/* Windows MIDI driver */
-class MidiDriver_WIN : public MidiDriver {
+class MidiDriver_WIN : public MidiDriver_MPU401 {
public:
int open(int mode);
void close();
@@ -304,7 +365,7 @@ MidiDriver *MidiDriver_WIN_create()
#include "morphos_sound.h"
/* MorphOS MIDI driver */
-class MidiDriver_ETUDE:public MidiDriver {
+class MidiDriver_ETUDE:public MidiDriver_MPU401 {
public:
int open(int mode);
void close();
@@ -515,7 +576,7 @@ MidiDriver *MidiDriver_ETUDE_create()
#define SEQ_MIDIPUTC 5
#define SPECIAL_CHANNEL 9
-class MidiDriver_SEQ:public MidiDriver {
+class MidiDriver_SEQ:public MidiDriver_MPU401 {
public:
MidiDriver_SEQ();
int open(int mode);
@@ -655,7 +716,7 @@ MidiDriver *MidiDriver_SEQ_create()
/* QuickTime MIDI driver */
-class MidiDriver_QT:public MidiDriver {
+class MidiDriver_QT:public MidiDriver_MPU401 {
public:
int open(int mode);
void close();
@@ -872,7 +933,7 @@ MidiDriver *MidiDriver_QT_create()
/* CoreAudio MIDI driver */
/* Based on code by Benjamin W. Zale */
-class MidiDriver_CORE:public MidiDriver {
+class MidiDriver_CORE:public MidiDriver_MPU401 {
public:
MidiDriver_CORE():au_MusicDevice(NULL), au_output(NULL) {
} int open(int mode);
@@ -979,7 +1040,7 @@ MidiDriver *MidiDriver_CORE_create()
#endif // __APPLE__
/* NULL driver */
-class MidiDriver_NULL:public MidiDriver {
+class MidiDriver_NULL:public MidiDriver_MPU401 {
public:
int open(int mode);
void close() { }
@@ -1051,7 +1112,7 @@ const char *MidiDriver::get_error_name(int error_code)
#define ADDR_DELIM ".:"
-class MidiDriver_ALSA:public MidiDriver {
+class MidiDriver_ALSA:public MidiDriver_MPU401 {
public:
MidiDriver_ALSA();
int open(int mode);
diff --git a/sound/mididrv.h b/sound/mididrv.h
index 6544f7f885..49fff19fd5 100644
--- a/sound/mididrv.h
+++ b/sound/mididrv.h
@@ -106,6 +106,12 @@ public:
send((range << 16) | ( 6 << 8) | (0xB0 | channel));
send(( 0 << 16) | ( 38 << 8) | (0xB0 | channel));
}
+
+ virtual void sysEx_customInstrument (byte channel, uint32 type, byte *instr) { }
+
+ // Timing functions - MidiDriver now operates timers
+ virtual void setTimerCallback (void *timer_param, void (*timer_proc) (void *)) = 0;
+ virtual uint32 getBaseTempo (void) = 0;
};
@@ -122,12 +128,14 @@ enum {
MD_COREAUDIO = 7,
MD_MIDIEMU = 8,
MD_ALSA = 9,
+ MD_ADLIB = 10
};
/* Factory functions => no need to include the specific classes
* in this header => faster compile */
extern MidiDriver *MidiDriver_NULL_create();
+extern MidiDriver *MidiDriver_ADLIB_create();
extern MidiDriver *MidiDriver_WIN_create();
extern MidiDriver *MidiDriver_TIMIDITY_create();
extern MidiDriver *MidiDriver_SEQ_create();
diff --git a/sound/midistreamer.cpp b/sound/midistreamer.cpp
index fcd4109c81..724b0852c5 100644
--- a/sound/midistreamer.cpp
+++ b/sound/midistreamer.cpp
@@ -37,6 +37,7 @@ private:
int _event_count;
int _event_index;
+ long _driver_tempo;
long _tempo;
uint16 _ticks_per_beat;
long _delay;
@@ -44,8 +45,8 @@ private:
volatile bool _active;
uint32 property(int prop, uint32 param);
- static int timer_thread (void *param);
- void on_timer (void);
+ static void timer_thread (void *param);
+ void on_timer();
public:
MidiStreamer (MidiDriver *target);
@@ -56,6 +57,9 @@ public:
void pause(bool p) { _paused = p; }
void set_stream_callback(void *param, StreamCallback *sc);
void setPitchBendRange (byte channel, uint range) { _target->setPitchBendRange (channel, range); }
+
+ void setTimerCallback (void *timer_param, void (*timer_proc) (void *)) { }
+ uint32 getBaseTempo (void) { return _target->getBaseTempo(); }
};
MidiStreamer::MidiStreamer (MidiDriver *target) :
@@ -82,7 +86,7 @@ void MidiStreamer::set_stream_callback (void *param, StreamCallback *sc)
_event_index = 0;
}
}
-
+/*
int MidiStreamer::timer_thread (void *param) {
MidiStreamer *mid = (MidiStreamer *) param;
int old_time, cur_time;
@@ -109,10 +113,14 @@ int MidiStreamer::timer_thread (void *param) {
mid->_active = false;
return 0;
}
+*/
+void MidiStreamer::timer_thread (void *param) {
+ ((MidiStreamer *) param)->on_timer();
+}
void MidiStreamer::on_timer()
{
- _delay += 10000;
+ _delay += _driver_tempo; // 10000;
while (true) {
if (_event_index >= _event_count) {
_event_count = _stream_proc (_stream_param, _events, ARRAYSIZE (_events));
@@ -145,18 +153,17 @@ int MidiStreamer::open (int mode)
if (res && res != MERR_ALREADY_OPEN)
return res;
- // Wait for existing timer thread to shut down.
- while (_active);
-
_event_index = _event_count = _delay = 0;
_mode = mode;
_paused = false;
- _active = true;
if (mode == MO_SIMPLE)
return 0;
- g_system->create_thread (timer_thread, this);
+// g_system->create_thread (timer_thread, this);
+ _driver_tempo = _target->getBaseTempo() / 500;
+
+ _target->setTimerCallback (this, &timer_thread);
return 0;
}
@@ -164,6 +171,15 @@ void MidiStreamer::close()
{
if (!_mode)
return;
+
+ _target->setTimerCallback (NULL, NULL);
+
+ // Turn off all notes on all channels,
+ // just to catch anything still playing.
+ int i;
+ for (i = 0; i < 16; ++i)
+ _target->send ((0x7B << 8) | 0xB0 | i);
+
_mode = 0;
_paused = true;
}