diff options
-rw-r--r-- | scumm.dsp | 4 | ||||
-rw-r--r-- | scumm.vcproj | 3 | ||||
-rw-r--r-- | scumm/imuse.cpp | 13 | ||||
-rw-r--r-- | scumm/imuse_player.cpp | 4 | ||||
-rw-r--r-- | scumm/midiparser_eup.cpp | 193 | ||||
-rw-r--r-- | scumm/module.mk | 1 | ||||
-rw-r--r-- | scumm/scummvm.cpp | 6 | ||||
-rw-r--r-- | scumm/sound.cpp | 19 |
8 files changed, 221 insertions, 22 deletions
@@ -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; } |