From e5bfead3458661140d17863ffe33af5f71638387 Mon Sep 17 00:00:00 2001 From: nukeykt Date: Sat, 23 Dec 2017 23:01:29 +0900 Subject: SCUMM HE: Use Miles AdLib driver --- audio/miles_adlib.cpp | 9 ++ devtools/scumm-md5.txt | 2 +- engines/scumm/detection_tables.h | 1 + engines/scumm/he/sound_he.cpp | 4 + engines/scumm/module.mk | 1 + engines/scumm/music.h | 7 ++ engines/scumm/players/player_he.cpp | 243 ++++++++++++++++++++++++++++++++++++ engines/scumm/players/player_he.h | 76 +++++++++++ engines/scumm/scumm-md5.h | 5 +- engines/scumm/scumm.cpp | 5 + 10 files changed, 350 insertions(+), 3 deletions(-) create mode 100644 engines/scumm/players/player_he.cpp create mode 100644 engines/scumm/players/player_he.h diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp index 790e38932d..4bdb770323 100644 --- a/audio/miles_adlib.cpp +++ b/audio/miles_adlib.cpp @@ -1035,6 +1035,15 @@ void MidiDriver_Miles_AdLib::pitchBendChange(byte midiChannel, byte parameter1, return; } _midiChannels[midiChannel].currentPitchBender = parameter1 | (parameter2 << 7); + for (byte virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) { + if (_virtualFmVoices[virtualFmVoice].inUse) { + // used + if (_virtualFmVoices[virtualFmVoice].actualMidiChannel == midiChannel) { + // by our current MIDI channel -> update + updatePhysicalFmVoice(virtualFmVoice, true, kMilesAdLibUpdateFlags_Reg_A0); + } + } + } } void MidiDriver_Miles_AdLib::setRegister(int reg, int value) { diff --git a/devtools/scumm-md5.txt b/devtools/scumm-md5.txt index ab32653da4..7e7d0accb7 100644 --- a/devtools/scumm-md5.txt +++ b/devtools/scumm-md5.txt @@ -772,7 +772,7 @@ puttmoon Putt-Putt Goes to the Moon 9c92eeaf517a31b7221ec2546ab669fd -1 en Windows HE 70 - - khalek 3c4c471342bd95505a42334367d8f127 12161 ru Windows HE 70 - - sev - aa6a91b7f6f119d1b7b1f2a4c9e24d59 6233 en DOS - Demo - + aa6a91b7f6f119d1b7b1f2a4c9e24d59 6233 en DOS Demo Demo - 4af4a6b248103c1fe9edef619677f540 -1 en Mac - Demo - khalek 9c143c5905055d5df7a0f014ab379aee -1 en Windows HE 70 Demo - khalek diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index 65891e0b0d..ce8eecfd4d 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -284,6 +284,7 @@ static const GameSettings gameVariantsTable[] = { {"fbear", "HE 70", 0, GID_FBEAR, 6, 70, MDT_NONE, GF_USE_KEY, Common::kPlatformWindows, GUIO2(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI)}, {"puttmoon", "", 0, GID_PUTTMOON, 6, 62, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO1(GUIO_NOLAUNCHLOAD)}, + {"puttmoon", "Demo", 0, GID_HEGAME, 6, 62, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO1(GUIO_NOLAUNCHLOAD)}, {"puttmoon", "HE 70", 0, GID_PUTTMOON, 6, 70, MDT_NONE, GF_USE_KEY, Common::kPlatformWindows, GUIO2(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI)}, {"puttputt", "HE 60", 0, GID_HEGAME, 6, 60, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO1(GUIO_NOLAUNCHLOAD)}, diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp index 9da3641064..07a81f5310 100644 --- a/engines/scumm/he/sound_he.cpp +++ b/engines/scumm/he/sound_he.cpp @@ -765,6 +765,10 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags, _vm->_imuse->stopSound(_currentMusic); _currentMusic = soundID; _vm->_imuse->startSoundWithNoteOffset(soundID, heOffset); + } else if (_vm->_musicEngine) { + _vm->_musicEngine->stopSound(_currentMusic); + _currentMusic = soundID; + _vm->_musicEngine->startSoundWithTrackID(soundID, heOffset); } } } diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index fee61ec29f..f5ba4bac2b 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -38,6 +38,7 @@ MODULE_OBJS := \ palette.o \ players/player_ad.o \ players/player_apple2.o \ + players/player_he.o \ players/player_mac.o \ players/player_mod.o \ players/player_nes.o \ diff --git a/engines/scumm/music.h b/engines/scumm/music.h index 9404ce7af3..06f45fe0e0 100644 --- a/engines/scumm/music.h +++ b/engines/scumm/music.h @@ -54,6 +54,13 @@ public: */ virtual void startSound(int sound) = 0; + /** + * Start playing the sound with the given id and track id. + * @param sound the sound to start + * @param track the track to start + */ + virtual void startSoundWithTrackID(int sound, int track) { startSound(sound); } + /** * Stop playing the sound with the given id. * @param sound the sound to stop diff --git a/engines/scumm/players/player_he.cpp b/engines/scumm/players/player_he.cpp new file mode 100644 index 0000000000..163f4f13f6 --- /dev/null +++ b/engines/scumm/players/player_he.cpp @@ -0,0 +1,243 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifdef ENABLE_HE + +#include "scumm/players/player_he.h" +#include "scumm/scumm.h" +#include "scumm/file.h" +#include "audio/miles.h" +#include "audio/midiparser.h" +#include "audio/mixer.h" +#include "common/memstream.h" + +namespace Scumm { +Player_HE::Player_HE(ScummEngine *scumm) : + _vm(scumm), + _currentMusic(-1), + _bank(NULL), + _parser(NULL), + _midi(NULL), + _masterVolume(256) { + + for (int chan = 0; chan < 16; chan++) + _channelVolume[chan] = 127; + + loadAdLibBank(); + + Common::MemoryReadStream *bankStream = new Common::MemoryReadStream(_bank, _bankSize); + + _midi = Audio::MidiDriver_Miles_AdLib_create("", "", bankStream); + if (!_midi) { + error("Player_HE::Player_HE: could not create midi driver"); + } + if (_midi->open() != 0) { + error("Player_HE::Player_HE: could not open midi driver"); + } +} + +Player_HE::~Player_HE() { + if (_parser) { + _parser->stopPlaying(); + delete _parser; + _parser = NULL; + } + if (_midi) { + _midi->setTimerCallback(0, 0); + _midi->close(); + delete _midi; + _midi = NULL; + } + if (_bank) { + free(_bank); + } +} + +void Player_HE::setMusicVolume(int vol) { + _masterVolume = vol; + for (int chan = 0; chan < 16; chan++) + { + byte volume = (_channelVolume[chan] * vol) / 256; + if (_midi) + _midi->send(0x07b0 | chan | (volume << 16)); + } +} + +void Player_HE::onTimer(void *data) { + Player_HE *player = (Player_HE*)data; + Common::StackLock lock(player->_mutex); + if (player->_parser) + player->_parser->onTimer(); +} + +void Player_HE::startSoundWithTrackID(int sound, int track) { + Common::StackLock lock(_mutex); + byte *ptr = _vm->getResourceAddress(rtSound, sound); + if (ptr == NULL) + return; + + if (_parser) { + _parser->stopPlaying(); + delete _parser; + } + _parser = MidiParser::createParser_XMIDI(); + _parser->setMidiDriver(this); + _parser->loadMusic(ptr + 40, 0); + _parser->setTrack(track); + _parser->setTimerRate(_midi->getBaseTempo()); + _midi->setTimerCallback(this, &Player_HE::onTimer); + + _currentMusic = sound; +} + +void Player_HE::stopSound(int sound) { + Common::StackLock lock(_mutex); + if (!_parser || _currentMusic != sound) + return; + _parser->stopPlaying(); + delete _parser; + _parser = NULL; +} + +void Player_HE::stopAllSounds() { + Common::StackLock lock(_mutex); + if (!_parser) + return; + _parser->stopPlaying(); + delete _parser; + _parser = NULL; +} + +int Player_HE::getSoundStatus(int sound) const { + Common::StackLock lock(_mutex); + return (_parser && _currentMusic == sound) ? _parser->isPlaying() : 0; +} + +int Player_HE::getMusicTimer() { + Common::StackLock lock(_mutex); + return _parser ? _parser->getTick() : 0; +} + +void Player_HE::loadAdLibBank() { + ScummFile file; + Common::String drvName; + char entryName[14]; + uint32 tag, entrySize, fileSize; + Common::String bankName; + + if (_vm->_game.id == GID_PUTTMOON) { + // Use GM bank + bankName = "FAT.AD"; + } else { + // Use MT32-like bank + bankName = "MIDPAK.AD"; + } + + const char *ptr = strchr(_vm->_filenamePattern.pattern, '.'); + if (ptr) { + drvName = Common::String(_vm->_filenamePattern.pattern, ptr - _vm->_filenamePattern.pattern + 1); + } else { + drvName = _vm->_filenamePattern.pattern; + drvName += '.'; + } + + drvName += "drv"; + + if (!file.open(drvName)) + error("Player_HE::loadAdLibBank(): could not open %s", drvName.c_str()); + + uint32 size = (uint32)file.size(); + + for (uint32 offset = 0; offset < size;) { + file.seek(offset, SEEK_SET); + if (size - offset < 31) + error("Player_HE::loadAdLibBank(): unexpected end of file"); + + tag = file.readUint32BE(); + entrySize = file.readUint32BE(); + if (size - offset < entrySize) + error("Player_HE::loadAdLibBank(): unexpected end of file"); + fileSize = entrySize - 31; + file.read(entryName, 13); + entryName[13] = 0; + + if (tag != MKTAG('F', 'I', 'L', 'E')) + error("Player_HE::loadAdLibBank(): unknown entry format"); + + if (entryName == bankName) { + _bank = (byte*)malloc(fileSize); + file.read(_bank, fileSize); + _bankSize = fileSize; + return; + } + + offset += entrySize; + } + error("Player_HE::loadAdLibBank(): could not find %s entry", bankName.c_str()); +} + +int Player_HE::open() { + if (_midi) + return _midi->open(); + return 0; +} + +bool Player_HE::isOpen() const { + if (_midi) + return _midi->isOpen(); + return false; +} + +void Player_HE::close() { + if (_midi) + _midi->close(); +} + +void Player_HE::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + if (_midi) + _midi->setTimerCallback(timerParam, timerProc); +} + +uint32 Player_HE::getBaseTempo() { + if (_midi) + return _midi->getBaseTempo(); + return 0; +} + +void Player_HE::send(uint32 b) { + byte chan = b & 0x0f; + byte cmd = b & 0xf0; + byte op1 = (b >> 8) & 0x7f; + byte op2 = (b >> 16) & 0x7f; + if (cmd == 0xb0 && op1 == 0x07) + { + _channelVolume[chan] = op2; + op2 = (op2 * _masterVolume) / 256; + b = (b & 0xffff) | (op2 << 16); + } + if (_midi) + _midi->send(b); +} + +} + +#endif diff --git a/engines/scumm/players/player_he.h b/engines/scumm/players/player_he.h new file mode 100644 index 0000000000..1405a056c9 --- /dev/null +++ b/engines/scumm/players/player_he.h @@ -0,0 +1,76 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_PLAYERS_PLAYER_HE_H +#define SCUMM_PLAYERS_PLAYER_HE_H + +#include "scumm/music.h" +#include "audio/mixer.h" +#include "audio/mididrv.h" +#include "common/mutex.h" + +class MidiParser; + +#ifdef ENABLE_HE + +namespace Scumm { +class ScummEngine; + +class Player_HE : public MusicEngine, public MidiDriver { +public: + Player_HE(ScummEngine *scumm); + ~Player_HE(); + void setMusicVolume(int vol); + void startSound(int sound) { startSoundWithTrackID(sound, 0); } + void startSoundWithTrackID(int sound, int track); + void stopSound(int sound); + void stopAllSounds(); + int getSoundStatus(int sound) const; + int getMusicTimer(); + + int open(); + bool isOpen() const; + void close(); + void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); + uint32 getBaseTempo(); + MidiChannel *allocateChannel() { return NULL; }; + MidiChannel *getPercussionChannel() { return NULL; }; + void send(uint32 b); + +private: + ScummEngine *_vm; + MidiParser *_parser; + MidiDriver *_midi; + Common::Mutex _mutex; + byte *_bank; + int _bankSize; + int _currentMusic; + int _masterVolume; + byte _channelVolume[16]; + static void onTimer(void *data); + void loadAdLibBank(); +}; +} + +#endif + +#endif diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index 50ed673a05..0afa5f53d7 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Fri Dec 22 04:43:47 2017 + This file was generated by the md5table tool on Thu Feb 01 01:05:11 2018 DO NOT EDIT MANUALLY! */ @@ -496,7 +496,7 @@ static const MD5Table md5table[] = { { "a9543ef0d79bcb47cd76ec197ad0a967", "puttmoon", "", "", -1, Common::EN_ANY, Common::kPlatform3DO }, { "a99c39ba65b6086be28aef576da69595", "spyozon", "", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, { "a9f2f04b1ecaab9495b59befffe9bf88", "pajama3", "", "Demo", -1, Common::EN_USA, Common::kPlatformUnknown }, - { "aa6a91b7f6f119d1b7b1f2a4c9e24d59", "puttmoon", "", "Demo", 6233, Common::EN_ANY, Common::kPlatformDOS }, + { "aa6a91b7f6f119d1b7b1f2a4c9e24d59", "puttmoon", "Demo", "Demo", 6233, Common::EN_ANY, Common::kPlatformDOS }, { "aa7a07d94ae853f6460be4ce0a1bf530", "monkey", "EGA", "EGA", -1, Common::FR_FRA, Common::kPlatformDOS }, { "aa81aa6d5545ce172fdba81f2e2f9d36", "puttzoo", "", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, { "aa8a0cb65f3afbbe2c14c3f9f92775a3", "monkey", "CD", "CD", 8955, Common::FR_FRA, Common::kPlatformDOS }, @@ -711,3 +711,4 @@ static const MD5Table md5table[] = { { "ff05c07990061d97647f059c48c1d05a", "zak", "V2", "V2", -1, Common::DE_DEU, Common::kPlatformAtariST }, { 0, 0, 0, 0, 0, Common::UNK_LANG, Common::kPlatformUnknown } }; + diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 77d82c88a6..fc64df4a1a 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -64,6 +64,7 @@ #include "scumm/players/player_v3m.h" #include "scumm/players/player_v4a.h" #include "scumm/players/player_v5m.h" +#include "scumm/players/player_he.h" #include "scumm/resource.h" #include "scumm/he/resource_he.h" #include "scumm/he/moonbase/moonbase.h" @@ -1954,6 +1955,10 @@ void ScummEngine::setupMusic(int midi) { // support this with the Player_AD code at the moment. The reason here // is that multi MIDI is supported internally by our iMuse output. _musicEngine = new Player_AD(this); +#ifdef ENABLE_HE + } else if (_game.platform == Common::kPlatformDOS && _sound->_musicType == MDT_ADLIB && _game.heversion >= 60) { + _musicEngine = new Player_HE(this); +#endif } else if (_game.version >= 3 && _game.heversion <= 62) { MidiDriver *nativeMidiDriver = 0; MidiDriver *adlibMidiDriver = 0; -- cgit v1.2.3