aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--scumm.dsp4
-rw-r--r--scumm.vcproj3
-rw-r--r--scumm/imuse.cpp13
-rw-r--r--scumm/imuse_player.cpp4
-rw-r--r--scumm/midiparser_eup.cpp193
-rw-r--r--scumm/module.mk1
-rw-r--r--scumm/scummvm.cpp6
-rw-r--r--scumm/sound.cpp19
8 files changed, 221 insertions, 22 deletions
diff --git a/scumm.dsp b/scumm.dsp
index d9c5f5a5b0..e3911f1c00 100644
--- a/scumm.dsp
+++ b/scumm.dsp
@@ -343,6 +343,10 @@ SOURCE=.\scumm\intern.h
# End Source File
# Begin Source File
+SOURCE=.\scumm\midiparser_eup.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\scumm\midiparser_ro.cpp
# End Source File
# Begin Source File
diff --git a/scumm.vcproj b/scumm.vcproj
index 8b719fe09c..e55a02a8a3 100644
--- a/scumm.vcproj
+++ b/scumm.vcproj
@@ -311,6 +311,9 @@
RelativePath=".\scumm\midiparser_ro.cpp">
</File>
<File
+ RelativePath=".\scumm\midiparser_eup.cpp">
+ </File>
+ <File
RelativePath="scumm\music.h">
</File>
<File
diff --git a/scumm/imuse.cpp b/scumm/imuse.cpp
index 57cf12b1e4..a02acd538c 100644
--- a/scumm/imuse.cpp
+++ b/scumm/imuse.cpp
@@ -88,6 +88,8 @@ byte *IMuseInternal::findStartOfSound(int sound) {
// Check for old-style headers first, like 'RO'
if (ptr[4] == 'R' && ptr[5] == 'O'&& ptr[6] != 'L')
return ptr + 4;
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return ptr + 8;
ptr += 8;
size = READ_BE_UINT32(ptr);
@@ -134,9 +136,12 @@ bool IMuseInternal::isMT32(int sound) {
return false;
}
- // Check old style headers, like 'RO'
+ // Old style 'RO' has equivalent properties to 'ROL'
if (ptr[4] == 'R' && ptr[5] == 'O')
return true;
+ // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return false;
return false;
}
@@ -170,9 +175,13 @@ bool IMuseInternal::isGM(int sound) {
return false;
}
- // Check old style headers, like 'RO'
+ // Old style 'RO' has equivalent properties to 'ROL'
if (ptr[4] == 'R' && ptr[5] == 'O')
return true;
+ // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
+ // FIXME: Right now we're pretending it's GM.
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return true;
return false;
}
diff --git a/scumm/imuse_player.cpp b/scumm/imuse_player.cpp
index 51315e0a6c..986dfb6340 100644
--- a/scumm/imuse_player.cpp
+++ b/scumm/imuse_player.cpp
@@ -39,6 +39,7 @@
////////////////////////////////////////
extern MidiParser *MidiParser_createRO();
+extern MidiParser *MidiParser_createEUP();
static uint read_word(byte *a) {
return (a[0] << 8) + a[1];
@@ -186,6 +187,9 @@ int Player::start_seq_sound(int sound, bool reset_vars) {
if (!memcmp (ptr, "RO", 2)) {
// Old style 'RO' resource
_parser = MidiParser_createRO();
+ } else if (!memcmp (ptr, "SO", 2)) {
+ // Euphony (FM Towns) resource
+ _parser = MidiParser_createEUP();
} else if (!memcmp(ptr, "FORM", 4)) {
// Humongous Games XMIDI resource
_parser = MidiParser::createParser_XMIDI();
diff --git a/scumm/midiparser_eup.cpp b/scumm/midiparser_eup.cpp
new file mode 100644
index 0000000000..ba0dd0ff93
--- /dev/null
+++ b/scumm/midiparser_eup.cpp
@@ -0,0 +1,193 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2003 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$
+ *
+ */
+
+#include "stdafx.h"
+#include "sound/midiparser.h"
+#include "sound/mididrv.h"
+#include "common/util.h"
+
+
+//////////////////////////////////////////////////
+//
+// The FM Towns Euphony version of MidiParser
+//
+//////////////////////////////////////////////////
+
+class MidiParser_EUP : public MidiParser {
+protected:
+ struct {
+ bool mute;
+ uint8 channel;
+ int8 volume;
+ int8 transpose;
+ } _presets[32];
+ bool _loop;
+ byte _presend; // Tracks which startup implied events have been sent.
+
+protected:
+ void parseNextEvent (EventInfo &info);
+ void resetTracking();
+
+public:
+ bool loadMusic (byte *data, uint32 size);
+};
+
+
+
+//////////////////////////////////////////////////
+//
+// MidiParser_EUP implementation
+//
+//////////////////////////////////////////////////
+
+void MidiParser_EUP::parseNextEvent (EventInfo &info) {
+ byte *pos = _position._play_pos;
+
+ // FIXME: The presend is for sending init events
+ // that aren't actually in the stream. This would
+ // be for, e.g., instrument setup. Right now, we
+ // don't actually use the instruments specified
+ // in the music header. We're sending fixed GM
+ // 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;
+ 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;
+ return;
+ }
+
+ while (true) {
+ byte cmd = *pos;
+ if ((cmd & 0xF0) == 0x90) {
+ byte preset = pos[1];
+ byte channel = _presets[preset].channel;
+ if (channel >= 16)
+ channel = cmd & 0x0F;
+ uint16 tick = pos[2] | ((uint16) pos[3] << 7);
+ int note = (int) pos[4] + _presets[preset].transpose;
+ int volume = (int) pos[5] + _presets[preset].volume;
+ pos += 6;
+ if ((*pos & 0xF0) == 0x80) {
+ if (!_presets[preset].mute) {
+ uint16 duration = pos[1] | (pos[2] << 4) | (pos[3] << 8) | (pos[4] << 12);
+ info.start = pos;
+ uint32 last = _position._last_event_tick;
+ info.delta = (tick < last) ? 0 : (tick - last);
+ info.event = 0x90 | channel;
+ info.length = duration;
+ info.basic.param1 = note;
+ info.basic.param2 = volume;
+ pos += 6;
+ break;
+ }
+ pos += 6;
+ }
+ } else if (cmd == 0xF2) {
+ // This is basically a "rest".
+ uint16 tick = pos[2] | (pos[3] << 7);
+ uint32 last = _position._last_event_tick;
+ info.start = pos;
+ info.delta = (tick < last) ? 0 : (tick - last);
+ info.event = 0xFF;
+ info.length = 0;
+ info.ext.type = 0x7F; // Bogus META event
+ info.ext.data = pos;
+ pos += 6;
+ break;
+ } else if (cmd == 0xF8) {
+ // TODO: Implement this.
+ pos += 6;
+ } else if (cmd == 0xFD || cmd == 0xFE) {
+ // End of track.
+ if (_loop && false) {
+ // TODO: Implement this.
+ } else {
+ info.start = pos;
+ info.delta = 0;
+ info.event = 0xFF;
+ info.length = 0;
+ info.ext.type = 0x2F;
+ info.ext.data = pos;
+ pos = 0;
+ break;
+ }
+ } else {
+ printf ("Unknown Euphony music event 0x%02X\n", (int) cmd);
+ memset (&info, 0, sizeof(info));
+ pos = 0;
+ break;
+ }
+ }
+ _position._play_pos = pos;
+}
+
+bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
+ unloadMusic();
+ byte *pos = data;
+
+ if (memcmp (pos, "SO", 2)) {
+ printf ("Warning: 'SO' header expected but found '%c%c' instead.\n", pos[0], pos[1]);
+ return false;
+ }
+
+ byte numInstruments = pos[16];
+ pos += (16 + 2 + numInstruments * 48);
+
+ for (int i = 0; i < 32; ++i) {
+ _presets[i].mute = ((int8) pos[i] == 0);
+ _presets[i].channel = pos[i+32];
+ _presets[i].volume = (int8) pos[i+64];
+ _presets[i].transpose = (int8) pos[i+96];
+ }
+ pos += 32 * 4; // Jump past presets
+ pos += 8; // Unknown bytes
+ pos += 6; // Instrument-to-channel mapping (not supported yet)
+ pos += 4; // Skip the music size for now.
+ pos++; // Unknown byte
+ byte tempo = *pos++;
+ _loop = (*pos++ != 1);
+ pos++; // Unknown byte
+
+ _num_tracks = 1;
+ _ppqn = 120;
+ _tracks[0] = pos;
+
+ // Note that we assume the original data passed in
+ // will persist beyond this call, i.e. we do NOT
+ // copy the data to our own buffer. Take warning....
+ resetTracking();
+ setTempo (1000000 * 60 / tempo);
+ setTrack (0);
+ return true;
+}
+
+void MidiParser_EUP::resetTracking() {
+ MidiParser::resetTracking();
+ _presend = 1;
+}
+
+MidiParser *MidiParser_createEUP() { return new MidiParser_EUP; }
diff --git a/scumm/module.mk b/scumm/module.mk
index 755b8a5a43..1d5a606e78 100644
--- a/scumm/module.mk
+++ b/scumm/module.mk
@@ -19,6 +19,7 @@ SCUMM_OBJS = \
scumm/instrument.o \
scumm/help.o \
scumm/midiparser_ro.o \
+ scumm/midiparser_eup.o \
scumm/nut_renderer.o \
scumm/object.o \
scumm/player_v1.o\
diff --git a/scumm/scummvm.cpp b/scumm/scummvm.cpp
index 9751856a70..408e3f9dae 100644
--- a/scumm/scummvm.cpp
+++ b/scumm/scummvm.cpp
@@ -91,11 +91,11 @@ static const TargetSettings scumm_settings[] = {
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_PCSPK,
+ {"zak256", "Zak McKracken and the Alien Mindbenders (256)", GID_ZAK256, 3, MDT_ADLIB,
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_NONE,
+ {"loomTowns", "Loom (FM Towns)", GID_LOOM, 3, MDT_ADLIB,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
/* Scumm Version 4 */
@@ -723,7 +723,7 @@ Scumm::Scumm (GameDetector *detector, OSystem *syst)
_imuse->property(IMuse::PROP_OLD_ADLIB_INSTRUMENTS, (_features & GF_SMALL_HEADER) ? 1 : 0);
_imuse->property(IMuse::PROP_MULTI_MIDI, detector->_multi_midi && _midiDriver != MD_NULL);
_imuse->property(IMuse::PROP_NATIVE_MT32, detector->_native_mt32);
- if (_features & GF_HUMONGOUS) {
+ if (_features & GF_HUMONGOUS || _features & GF_FMTOWNS) {
_imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
_imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
}
diff --git a/scumm/sound.cpp b/scumm/sound.cpp
index 76374c414c..9138ce1a99 100644
--- a/scumm/sound.cpp
+++ b/scumm/sound.cpp
@@ -384,23 +384,8 @@ void Sound::playSound(int soundID) {
}
case 1: { // Music (Euphony format)
- int numInstruments = *(ptr + 0x14);
- int tuneSize = 0, tempo = 0;
-
- ptr += (0x16 + (numInstruments * 48)); // Skip instrument definitions
- ptr += (32*4); // Skip preset values (mute, channel, volume, transpose)
- ptr += 8; // (Unknown)
-
- ptr += 6; // Instrument channel's. Always 6 bytes according to the disassembly.
-
- tuneSize = READ_LE_UINT32(ptr);
- ptr += 5;
-
- tempo = *ptr++;
- ptr += 2;
- // Music data begins here
-
- warning("Euphony tune #%d unsupported", soundID);
+ if (_scumm->_musicEngine)
+ _scumm->_musicEngine->startSound (soundID);
break;
}