diff --git a/README b/README
index d536097ae0a..20ded23d518 100644
--- a/README
+++ b/README
@@ -567,9 +567,10 @@ emulation. MIDI may not be available on all operating systems or may need
manual configuration. If you ARE using MIDI, you have several different
choices of output, depending on your operating system and configuration.
- adlib - Uses internal Adlib Emulation (default)
+ adlib - Uses internal Adlib Emulation (default)
pcjr - Uses internal PCjr Emulation
pcspk - Uses internal PC Speaker Emulation
+ towns - Uses FM-Towns YM2612 Emulation
windows - Windows MIDI. Uses built-in sequencer, for Windows users
seq - Uses /dev/sequencer for MIDI, *nix users. See below.
qt - Quicktime sound, for Macintosh users.
diff --git a/backends/midi/ym2612.cpp b/backends/midi/ym2612.cpp
new file mode 100644
index 00000000000..1da6f64c37e
--- /dev/null
+++ b/backends/midi/ym2612.cpp
@@ -0,0 +1,31 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2003 The ScummVM project
+ *
+ * YM2612 tone generation code written by Tomoaki Hayasaka.
+ * Used under the terms of the GNU General Public License.
+ * Adpated to ScummVM by Jamieson Christian.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ */
+
+// Real implementation coming soon! :)
+
+#include "sound/mididrv.h"
+
+MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer) {
+ return 0;
+}
diff --git a/backends/module.mk b/backends/module.mk
index bb10fa7bb51..80030f07815 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -11,7 +11,8 @@ MODULE_OBJS := \
backends/midi/quicktime.o \
backends/midi/seq.o \
backends/midi/alsa.o \
- backends/midi/windows.o
+ backends/midi/windows.o \
+ backends/midi/ym2612.o
MODULE_DIRS += \
backends \
diff --git a/base/gameDetector.cpp b/base/gameDetector.cpp
index d1c8bde1d1c..7c73715da12 100644
--- a/base/gameDetector.cpp
+++ b/base/gameDetector.cpp
@@ -676,12 +676,15 @@ bool GameDetector::detectMain() {
if (_game.midi & MDT_PREFER_NATIVE)
_midi_driver = getMidiDriverType();
else
- _midi_driver = MD_ADLIB;
+ _midi_driver = MD_TOWNS;
}
bool nativeMidiDriver =
(_midi_driver != MD_NULL && _midi_driver != MD_ADLIB &&
- _midi_driver != MD_PCSPK && _midi_driver != MD_PCJR);
+ _midi_driver != MD_PCSPK && _midi_driver != MD_PCJR &&
+ _midi_driver != MD_TOWNS);
if (nativeMidiDriver && !(_game.midi & MDT_NATIVE))
+ _midi_driver = MD_TOWNS;
+ if (_midi_driver == MD_TOWNS && !(_game.midi & MDT_TOWNS))
_midi_driver = MD_ADLIB;
if (_midi_driver == MD_ADLIB && !(_game.midi & MDT_ADLIB))
_midi_driver = MD_PCJR;
@@ -770,12 +773,15 @@ MidiDriver *GameDetector::createMidi() {
switch(drv) {
case MD_NULL: return MidiDriver_NULL_create();
+
// In the case of Adlib, we won't specify anything.
// IMuse is designed to set up its own Adlib driver
// if need be, and we only have to specify a native
// driver.
case MD_ADLIB: return NULL;
+ case MD_TOWNS: return MidiDriver_YM2612_create(g_engine->_mixer);
+
// Right now PC Speaker and PCjr are handled
// outside the MidiDriver architecture, so
// don't create anything for now.
diff --git a/base/gameDetector.h b/base/gameDetector.h
index 038a7681c41..a78288e437c 100644
--- a/base/gameDetector.h
+++ b/base/gameDetector.h
@@ -70,8 +70,9 @@ enum MidiDriverType {
MDT_NONE = 0,
MDT_PCSPK = 1, // MD_PCSPK and MD_PCJR
MDT_ADLIB = 2, // MD_ADLIB
- MDT_NATIVE = 4, // Everything else
- MDT_PREFER_NATIVE = 8
+ MDT_TOWNS = 4, // MD_TOWNS
+ MDT_NATIVE = 8, // Everything else
+ MDT_PREFER_NATIVE = 16
};
struct TargetSettings {
diff --git a/dists/msvc6/scummvm.dsp b/dists/msvc6/scummvm.dsp
index d7081171107..d5a9f6581f9 100644
--- a/dists/msvc6/scummvm.dsp
+++ b/dists/msvc6/scummvm.dsp
@@ -540,6 +540,10 @@ SOURCE=..\..\backends\midi\null.cpp
SOURCE=..\..\backends\midi\windows.cpp
# End Source File
+# Begin Source File
+
+SOURCE=..\..\backends\midi\ym2612.cpp
+# End Source File
# End Group
# End Group
# End Group
diff --git a/dists/msvc7/scummvm.vcproj b/dists/msvc7/scummvm.vcproj
index cb9366490a0..d7b29f98bfb 100644
--- a/dists/msvc7/scummvm.vcproj
+++ b/dists/msvc7/scummvm.vcproj
@@ -355,6 +355,9 @@
+
+
clear();
- return player->startSound(sound, driver);
+ return player->startSound(sound, driver, _direct_passthrough);
}
@@ -1117,10 +1118,14 @@ uint32 IMuseInternal::property(int prop, uint32 value) {
break;
case IMuse::PROP_RECYCLE_PLAYERS:
- if (value > 0 && value <= ARRAYSIZE(_players))
- _recycle_players = (value != 0);
+ _recycle_players = (value != 0);
+ break;
+
+ case IMuse::PROP_DIRECT_PASSTHROUGH:
+ _direct_passthrough = (value != 0);
break;
}
+
return 0;
}
diff --git a/scumm/imuse.h b/scumm/imuse.h
index be19ba03722..acebe405294 100644
--- a/scumm/imuse.h
+++ b/scumm/imuse.h
@@ -51,12 +51,13 @@ public:
~IMuse();
enum {
- PROP_TEMPO_BASE = 1,
- PROP_NATIVE_MT32 = 2,
- PROP_MULTI_MIDI = 3,
- PROP_OLD_ADLIB_INSTRUMENTS = 4,
- PROP_LIMIT_PLAYERS = 5,
- PROP_RECYCLE_PLAYERS = 6
+ PROP_TEMPO_BASE,
+ PROP_NATIVE_MT32,
+ PROP_MULTI_MIDI,
+ PROP_OLD_ADLIB_INSTRUMENTS,
+ PROP_LIMIT_PLAYERS,
+ PROP_RECYCLE_PLAYERS,
+ PROP_DIRECT_PASSTHROUGH
};
void on_timer(MidiDriver *midi);
diff --git a/scumm/imuse_internal.h b/scumm/imuse_internal.h
index 0265df65d52..e7e91b3f22c 100644
--- a/scumm/imuse_internal.h
+++ b/scumm/imuse_internal.h
@@ -60,10 +60,6 @@ class ScummEngine;
#define TICKS_PER_BEAT 480
-#define IMUSE_SYSEX_ID 0x7D
-#define ROLAND_SYSEX_ID 0x41
-#define PERCUSSION_CHANNEL 9
-
#define TRIGGER_ID 0
#define COMMAND_ID 1
@@ -158,6 +154,7 @@ protected:
protected:
MidiDriver *_midi;
MidiParser *_parser;
+ bool _passThrough; // Only respond to EOT, all else direct to MidiDriver
Part *_parts;
bool _active;
@@ -259,7 +256,7 @@ public:
void setSpeed(byte speed);
int setTranspose(byte relative, int b);
int setVolume(byte vol);
- bool startSound(int sound, MidiDriver *midi);
+ bool startSound(int sound, MidiDriver *midi, bool passThrough);
int getMusicTimer() const;
public:
@@ -366,8 +363,9 @@ protected:
int _tempoFactor;
- int _player_limit; // Limits how many simultaneous music tracks are played
- bool _recycle_players; // Can we stop a player in order to start another one?
+ int _player_limit; // Limits how many simultaneous music tracks are played
+ bool _recycle_players; // Can we stop a player in order to start another one?
+ bool _direct_passthrough; // Pass data direct to MidiDriver (no interactivity)
uint _queue_end, _queue_pos, _queue_sound;
byte _queue_adding;
diff --git a/scumm/imuse_player.cpp b/scumm/imuse_player.cpp
index 69b577ed43e..23048341b3c 100644
--- a/scumm/imuse_player.cpp
+++ b/scumm/imuse_player.cpp
@@ -39,6 +39,11 @@ namespace Scumm {
//
////////////////////////////////////////
+#define IMUSE_SYSEX_ID 0x7D
+#define YM2612_SYSEX_ID 0x7C
+#define ROLAND_SYSEX_ID 0x41
+#define PERCUSSION_CHANNEL 9
+
extern MidiParser *MidiParser_createRO();
extern MidiParser *MidiParser_createEUP();
@@ -85,7 +90,7 @@ Player::~Player() {
}
}
-bool Player::startSound(int sound, MidiDriver *midi) {
+bool Player::startSound(int sound, MidiDriver *midi, bool passThrough) {
void *ptr;
int i;
@@ -111,6 +116,7 @@ bool Player::startSound(int sound, MidiDriver *midi) {
_pan = 0;
_transpose = 0;
_detune = 0;
+ _passThrough = passThrough;
for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i)
_parameterFaders[i].init();
@@ -152,8 +158,11 @@ void Player::clear() {
debug (0, "Stopping music %d", _id);
#endif
- if (_parser)
+ if (_parser) {
_parser->unloadMusic();
+ delete _parser;
+ _parser = 0;
+ }
uninit_parts();
_se->ImFireAllTriggers(_id);
_active = false;
@@ -224,6 +233,11 @@ void Player::setSpeed(byte speed) {
}
void Player::send(uint32 b) {
+ if (_passThrough) {
+ _midi->send (b);
+ return;
+ }
+
byte cmd = (byte)(b & 0xF0);
byte chan = (byte)(b & 0x0F);
byte param1 = (byte)((b >> 8) & 0xFF);
@@ -333,8 +347,12 @@ void Player::sysEx(byte *p, uint16 len) {
byte buf[128];
Part *part;
+ if (_passThrough) {
+ _midi->sysEx (p, len);
+ return;
+ }
+
// Check SysEx manufacturer.
- // Roland is 0x41
a = *p++;
--len;
if (a != IMUSE_SYSEX_ID) {
@@ -346,6 +364,9 @@ void Player::sysEx(byte *p, uint16 len) {
if (part->clearToTransmit())
part->_instrument.send(part->_mc);
}
+ } else if (a == YM2612_SYSEX_ID) {
+ // FM-Towns custom instrument definition
+ _midi->sysEx_customInstrument (p[0], 'EUP ', p + 1);
} else {
warning("Unknown SysEx manufacturer 0x%02X", (int) a);
}
@@ -1124,10 +1145,8 @@ uint32 Player::getBaseTempo() {
}
void Player::metaEvent(byte type, byte *msg, uint16 len) {
- if (type == 0x2F) {
- _parser->unloadMusic();
+ if (type == 0x2F)
clear();
- }
}
diff --git a/scumm/midiparser_eup.cpp b/scumm/midiparser_eup.cpp
index a094ade5cea..8ba23c1a965 100644
--- a/scumm/midiparser_eup.cpp
+++ b/scumm/midiparser_eup.cpp
@@ -32,6 +32,8 @@ namespace Scumm {
*/
class MidiParser_EUP : public MidiParser {
protected:
+ byte _instruments[6][50]; // Two extra bytes for SysEx ID and channel #
+ byte _channel_instr[16];
struct {
byte *enable;
int8 *channel;
@@ -69,14 +71,22 @@ void MidiParser_EUP::parseNextEvent (EventInfo &info) {
// program changes to get a reasonable "one-size-
// fits-all" sound until we actually support the
// FM synthesis capabilities of FM Towns.
- if (_presend) {
- --_presend;
+ for (; _presend < 32; ++_presend) {
+ if (_channel_instr[_presend >> 1] == 0xFF) continue;
info.start = pos;
info.delta = 0;
- info.event = ((_presend & 1) ? 0xB0 : 0xC0) | (_presend >> 1);
- info.basic.param1 = ((_presend & 1) ? 7 : 0x38);
- info.basic.param2 = ((_presend & 1) ? 127 : 0);
- _presend = (_presend + 2) % 32;
+ if (_presend & 1) {
+ info.event = 0xB0;
+ info.basic.param1 = 7;
+ info.basic.param2 = 127;
+ } else {
+ byte *data = &_instruments[_channel_instr[_presend >> 1]][0];
+ data[1] = _presend >> 1;
+ info.event = 0xF0;
+ info.ext.data = data;
+ info.length = 48;
+ }
+ ++_presend;
return;
}
@@ -89,7 +99,17 @@ void MidiParser_EUP::parseNextEvent (EventInfo &info) {
channel = cmd & 0x0F;
uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _base_tick;
int note = (int) pos[4] + _presets.transpose[preset];
- int volume = (int) pos[5] + _presets.volume[preset];
+ int volume = (int) pos[5];
+ // HACK: Loom-Towns distaff tracks seem to
+ // contain zero-volume note events, so change
+ // those to full volume.
+ if (!volume)
+ volume = 127;
+ volume += _presets.volume[preset];
+ if (volume > 127)
+ volume = 127;
+ else if (volume < 0)
+ volume = 0;
pos += 6;
if (_presets.enable[preset]) {
uint16 duration = pos[1] | (pos[2] << 4);
@@ -148,7 +168,12 @@ bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
}
byte numInstruments = pos[16];
- pos += (16 + 2 + numInstruments * 48);
+ pos += 16 + 2;
+ for (int i = 0; i < numInstruments; ++i) {
+ _instruments[i][0] = 0x7C;
+ memcpy (&_instruments[i][2], pos, 48);
+ pos += 48;
+ }
// Load the prest pointers
_presets.enable = pos;
@@ -161,6 +186,10 @@ bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
pos += 32;
pos += 8; // Unknown bytes
+ for (i = 0; i < 16; ++i)
+ _channel_instr[i] = 0xFF;
+ for (i = 0; i < 6; ++i)
+ _channel_instr[pos[i]] = i;
pos += 6; // Instrument-to-channel mapping (not supported yet)
pos += 4; // Skip the music size for now.
pos++; // Unknown byte
@@ -183,7 +212,7 @@ bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
void MidiParser_EUP::resetTracking() {
MidiParser::resetTracking();
- _presend = 1;
+ _presend = 0;
_base_tick = 0;
}
diff --git a/scumm/scummvm.cpp b/scumm/scummvm.cpp
index fc8dfa16d0f..ad421fb38d6 100644
--- a/scumm/scummvm.cpp
+++ b/scumm/scummvm.cpp
@@ -92,15 +92,15 @@ static const TargetSettings scumm_settings[] = {
/* Scumm Version 3 */
{"indy3EGA", "Indiana Jones and the Last Crusade", GID_INDY3, 3, MDT_PCSPK | MDT_ADLIB,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, "00.LFL"},
- {"indy3Towns", "Indiana Jones and the Last Crusade (FM Towns)", GID_INDY3, 3, MDT_ADLIB,
+ {"indy3Towns", "Indiana Jones and the Last Crusade (FM Towns)", GID_INDY3, 3, MDT_TOWNS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
{"indy3", "Indiana Jones and the Last Crusade (256)", GID_INDY3, 3, MDT_PCSPK | MDT_ADLIB,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS, "00.LFL"},
- {"zak256", "Zak McKracken and the Alien Mindbenders (256)", GID_ZAK256, 3, MDT_ADLIB,
+ {"zak256", "Zak McKracken and the Alien Mindbenders (256)", GID_ZAK256, 3, MDT_TOWNS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
{"loom", "Loom", GID_LOOM, 3, MDT_PCSPK | MDT_ADLIB | MDT_NATIVE,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, "00.LFL"},
- {"loomTowns", "Loom (FM Towns)", GID_LOOM, 3, MDT_ADLIB,
+ {"loomTowns", "Loom (FM Towns)", GID_LOOM, 3, MDT_TOWNS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
/* Scumm Version 4 */
@@ -696,6 +696,8 @@ ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst)
_imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
_imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
}
+ if (_features & GF_FMTOWNS)
+ _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
_imuse->set_music_volume(_sound->_sound_volume_music);
}
}
diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp
index 3f175ce1f6c..d6ab13762cb 100644
--- a/sound/mididrv.cpp
+++ b/sound/mididrv.cpp
@@ -54,6 +54,7 @@ static const struct MidiDriverDescription midiDrivers[] = {
{"adlib", "Adlib", MD_ADLIB},
{"pcspk", "PC Speaker", MD_PCSPK},
{"pcjr", "IBM PCjr", MD_PCJR},
+ {"towns", "FM Towns", MD_TOWNS},
#endif
#if defined(__PALM_OS__)
diff --git a/sound/mididrv.h b/sound/mididrv.h
index 65043ff5f92..b82787b62d9 100644
--- a/sound/mididrv.h
+++ b/sound/mididrv.h
@@ -44,8 +44,8 @@ enum {
MD_ADLIB = 10,
MD_PCSPK = 11,
MD_PCJR = 12,
-
- MD_YPA1 = 100 // palmos
+ MD_TOWNS = 13,
+ MD_YPA1 = 14 // PalmOS
};
/**
@@ -144,16 +144,16 @@ public:
// Control Change messages
virtual void controlChange (byte control, byte value) = 0;
- virtual void modulationWheel (byte value) = 0;
- virtual void volume (byte value) = 0;
- virtual void panPosition (byte value) = 0;
+ virtual void modulationWheel (byte value) { controlChange (1, value); }
+ virtual void volume (byte value) { controlChange (7, value); }
+ virtual void panPosition (byte value) { controlChange (10, value); }
virtual void pitchBendFactor (byte value) = 0;
- virtual void detune (byte value) = 0;
- virtual void priority (byte value) = 0;
- virtual void sustain (bool value) = 0;
- virtual void effectLevel (byte value) = 0;
- virtual void chorusLevel (byte value) = 0;
- virtual void allNotesOff() = 0;
+ virtual void detune (byte value) { controlChange (17, value); }
+ virtual void priority (byte value) { controlChange (18, value); }
+ virtual void sustain (bool value) { controlChange (64, value ? 1 : 0); }
+ virtual void effectLevel (byte value) { controlChange (91, value); }
+ virtual void chorusLevel (byte value) { controlChange (93, value); }
+ virtual void allNotesOff() { controlChange (123, 0); }
// SysEx messages
virtual void sysEx_customInstrument (uint32 type, byte *instr) = 0;
@@ -169,6 +169,7 @@ extern MidiDriver *MidiDriver_QT_create();
extern MidiDriver *MidiDriver_CORE_create();
extern MidiDriver *MidiDriver_ETUDE_create();
extern MidiDriver *MidiDriver_ALSA_create();
+extern MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer);
extern MidiDriver *MidiDriver_YamahaPa1_create();
#endif
diff --git a/sound/mpu401.h b/sound/mpu401.h
index aef0ea9abb7..c97726e6fba 100644
--- a/sound/mpu401.h
+++ b/sound/mpu401.h
@@ -59,16 +59,7 @@ public:
// Control Change messages
void controlChange (byte control, byte value);
- void modulationWheel (byte value) { controlChange (1, value); }
- void volume (byte value) { controlChange (7, value); }
- void panPosition (byte value) { controlChange (10, value); }
void pitchBendFactor (byte value);
- void detune (byte value) { controlChange (17, value); }
- void priority (byte value) { controlChange (18, value); }
- void sustain (bool value) { controlChange (64, value ? 1 : 0); }
- void effectLevel (byte value) { controlChange (91, value); }
- void chorusLevel (byte value) { controlChange (93, value); }
- void allNotesOff() { controlChange (123, 0); }
// SysEx messages
void sysEx_customInstrument (uint32 type, byte *instr);