From 01c9b1706823b5a872f376a51a94ae3266dad69a Mon Sep 17 00:00:00 2001 From: Florian Kagerer Date: Thu, 7 Oct 2010 19:23:49 +0000 Subject: SCUMM/FM-TOWNS: improved sfx support for indy4 and monkey2 svn-id: r53052 --- engines/scumm/player_towns.cpp | 274 +++++++++++++++++++++++++++++++++-------- engines/scumm/player_towns.h | 71 +++++++++-- engines/scumm/saveload.h | 2 +- engines/scumm/scumm.cpp | 19 ++- engines/scumm/scumm.h | 4 +- engines/scumm/sound.cpp | 4 +- 6 files changed, 303 insertions(+), 71 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp index bb7985f6da..6d8dc9f847 100644 --- a/engines/scumm/player_towns.cpp +++ b/engines/scumm/player_towns.cpp @@ -29,7 +29,7 @@ namespace Scumm { -Player_Towns::Player_Towns(ScummEngine *vm) : _vm(vm) { +Player_Towns::Player_Towns(ScummEngine *vm, bool isVersion2) : _vm(vm), _v2(isVersion2), _numSoundMax(isVersion2 ? 256 : 200) { memset(_pcmCurrentSound, 0, sizeof(_pcmCurrentSound)); memset(&_ovrCur, 0, sizeof(SoundOvrParameters)); _soundOverride = 0; @@ -37,6 +37,22 @@ Player_Towns::Player_Towns(ScummEngine *vm) : _vm(vm) { _intf = 0; } +void Player_Towns::setSfxVolume(int vol) { + if (!_intf) + return; + _intf->setSoundEffectVolume(vol); +} + +int Player_Towns::getSoundStatus(int sound) const { + if (!_intf) + return 0; + for (int i = 1; i < 9; i++) { + if (_pcmCurrentSound[i].index == sound) + return _intf->callback(40, 0x3f + i) ? 1 : 0; + } + return 0; +} + void Player_Towns::saveLoadWithSerializer(Serializer *ser) { static const SaveLoadEntry pcmEntries[] = { MKLINE(PcmCurrentSound, index, sleInt16, VER(81)), @@ -67,7 +83,7 @@ void Player_Towns::saveLoadWithSerializer(Serializer *ser) { void Player_Towns::restoreAfterLoad() { for (int i = 1; i < 9; i++) { - if (!_pcmCurrentSound[i].index) + if (!_pcmCurrentSound[i].index || _pcmCurrentSound[i].index == 0xffff) continue; uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index); @@ -80,11 +96,14 @@ void Player_Towns::restoreAfterLoad() { if (ptr[13]) continue; - playPcmTrack(_pcmCurrentSound[i].index, ptr + 6, _pcmCurrentSound[i].velo, _pcmCurrentSound[i].pan, _pcmCurrentSound[i].note); + playPcmTrack(_pcmCurrentSound[i].index, ptr + 6, _pcmCurrentSound[i].velo, _pcmCurrentSound[i].pan, _pcmCurrentSound[i].note, _pcmCurrentSound[i].priority); } } -void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, int note) { +void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, int note, int priority) { + if (!_intf) + return; + const uint8 *ptr = data; const uint8 *sfxData = ptr + 16; @@ -97,9 +116,9 @@ void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, else velocity = ptr[8] >> 1; - int numChan = ptr[14]; + int numChan = _v2 ? 1 : ptr[14]; for (int i = 0; i < numChan; i++) { - int chan = getNextFreePcmChannel(sound, i); + int chan = getNextFreePcmChannel(sound, i, priority); if (!chan) return; @@ -126,6 +145,9 @@ void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, } void Player_Towns::stopPcmTrack(int sound) { + if (!_intf) + return; + for (int i = 1; i < 9; i++) { if (sound == _pcmCurrentSound[i].index || !sound) { _intf->callback(39, i + 0x3f); @@ -134,49 +156,50 @@ void Player_Towns::stopPcmTrack(int sound) { } } -int Player_Towns::getNextFreePcmChannel(int sound, int sfxChanRelIndex) { - int chan = 0; - for (int i = 8; i; i--) { - if (!_pcmCurrentSound[i].index) { - chan = i; - continue; - } - - if (_intf->callback(40, i + 0x3f)) - continue; +int Player_Towns::getNextFreePcmChannel(int sound, int sfxChanRelIndex, int priority) { + if (!_intf) + return 0; - chan = i; - _vm->_sound->stopSound(_pcmCurrentSound[chan].index); - } + int chan = 0; - if (!chan) { - uint16 l = 0xffff; - uint8 *ptr = 0; + if (_v2 && priority > 255) { + chan = 8; + if (_intf->callback(40, 0x47)) + _vm->_sound->stopSound(_pcmCurrentSound[chan].index); + } else { for (int i = 8; i; i--) { - ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index) + 6; - uint16 a = READ_LE_UINT16(ptr + 10); - if (a <= l) { + if (!_pcmCurrentSound[i].index) { chan = i; - l = a; + continue; } - } - ptr = _vm->getResourceAddress(rtSound, sound) + 6; - if (l <= READ_LE_UINT16(ptr + 10)) + if (_intf->callback(40, i + 0x3f)) + continue; + + chan = i; _vm->_sound->stopSound(_pcmCurrentSound[chan].index); - else - chan = 0; + } + + if (!chan) { + for (int i = 1; i < 9; i++) { + if (priority >= _pcmCurrentSound[i].priority) + chan = i; + } + if (chan) + _vm->_sound->stopSound(_pcmCurrentSound[chan].index); + } } if (chan) { _pcmCurrentSound[chan].index = sound; _pcmCurrentSound[chan].chan = sfxChanRelIndex; + _pcmCurrentSound[chan].priority = priority; } return chan; } -Player_Towns_v1::Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer) : Player_Towns(vm) { +Player_Towns_v1::Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer) : Player_Towns(vm, false) { _cdaCurrentSound = _eupCurrentSound = _cdaNumLoops = 0; _cdaForceRestart = 0; _cdaVolLeft = _cdaVolRight = 0; @@ -184,8 +207,8 @@ Player_Towns_v1::Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer) : Player_ _eupVolLeft = _eupVolRight = 0; if (_vm->_game.version == 3) { - _soundOverride = new SoundOvrParameters[200]; - memset(_soundOverride, 0, 200 * sizeof(SoundOvrParameters)); + _soundOverride = new SoundOvrParameters[_numSoundMax]; + memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters)); } _eupLooping = false; @@ -222,15 +245,11 @@ void Player_Towns_v1::setMusicVolume(int vol) { _driver->setMusicVolume(vol); } -void Player_Towns_v1::setSfxVolume(int vol) { - _driver->setSoundEffectVolume(vol); -} - void Player_Towns_v1::startSound(int sound) { uint8 *ptr = _vm->getResourceAddress(rtSound, sound); if (_vm->_game.version != 3) { ptr += 2; - } else if (_soundOverride && sound > 0 && sound < 200) { + } else if (_soundOverride && sound > 0 && sound < _numSoundMax) { memcpy(&_ovrCur, &_soundOverride[sound], sizeof(SoundOvrParameters)); memset(&_soundOverride[sound], 0, sizeof(SoundOvrParameters)); } @@ -238,7 +257,7 @@ void Player_Towns_v1::startSound(int sound) { int type = ptr[13]; if (type == 0) { - playPcmTrack(sound, ptr + 6); + playPcmTrack(sound, ptr + 6, 0, 64, 0, READ_LE_UINT16(ptr + 10)); } else if (type == 1) { playEuphonyTrack(sound, ptr + 6); } else if (type == 2) { @@ -280,11 +299,7 @@ int Player_Towns_v1::getSoundStatus(int sound) const { return _vm->_sound->pollCD(); if (sound == _eupCurrentSound) return _driver->parserIsPlaying() ? 1 : 0; - for (int i = 1; i < 9; i++) { - if (_pcmCurrentSound[i].index == sound) - return _driver->soundEffectIsPlaying(i + 0x3f) ? 1 : 0; - } - return 0; + return Player_Towns::getSoundStatus(sound); } int32 Player_Towns_v1::doCommand(int numargs, int args[]) { @@ -334,14 +349,14 @@ void Player_Towns_v1::setVolumeCD(int left, int right) { } void Player_Towns_v1::setSoundVolume(int sound, int left, int right) { - if (_soundOverride && sound > 0 && sound < 200) { + if (_soundOverride && sound > 0 && sound < _numSoundMax) { _soundOverride[sound].vLeft = left; _soundOverride[sound].vRight = right; } } void Player_Towns_v1::setSoundNote(int sound, int note) { - if (_soundOverride && sound > 0 && sound < 200) + if (_soundOverride && sound > 0 && sound < _numSoundMax) _soundOverride[sound].note = note; } @@ -404,8 +419,6 @@ void Player_Towns_v1::restoreAfterLoad() { Player_Towns::restoreAfterLoad(); } - - void Player_Towns_v1::restartLoopingSounds() { if (_cdaNumLoops && !_cdaForceRestart) _cdaForceRestart = 1; @@ -441,6 +454,7 @@ void Player_Towns_v1::startSoundEx(int sound, int velo, int pan, int note) { velo = velo ? (velo * ptr[14] + 50) / 100 : ptr[14]; velo = CLIP(velo, 1, 255); + uint16 pri = READ_LE_UINT16(ptr + 10); if (ptr[13] == 0) { velo >>= 1; @@ -450,7 +464,7 @@ void Player_Towns_v1::startSoundEx(int sound, int velo, int pan, int note) { pan = pan ? (((pan << 7) - pan) + 50) / 100 : 64; - playPcmTrack(sound, ptr + 6, velo, pan, note); + playPcmTrack(sound, ptr + 6, velo, pan, note, pri); } else if (ptr[13] == 2) { int volLeft = velo; @@ -567,5 +581,163 @@ void Player_Towns_v1::playCdaTrack(int sound, const uint8 *data, bool skipTrackV _cdaCurrentSound = sound; } -} // End of namespace Scumm +Player_Towns_v2::Player_Towns_v2(ScummEngine *vm, IMuse *imuse, Audio::Mixer *mixer, bool disposeIMuse) : Player_Towns(vm, true), _imuse(imuse), _imuseDispose(disposeIMuse) { + _soundOverride2 = new SoundOvrParameters2[_numSoundMax]; + memset(_soundOverride2, 0, _numSoundMax * sizeof(SoundOvrParameters2)); + _sblData = new uint8[0x4000]; + _intf = new TownsAudioInterface(mixer, 0); +} + +Player_Towns_v2::~Player_Towns_v2() { + delete[] _sblData; + delete[] _soundOverride; + delete _intf; + if (_imuseDispose) + delete _imuse; +} + +bool Player_Towns_v2::init() { + if (!_intf) + return false; + + if (!_intf->init()) + return false; + + _intf->callback(33, 8); + _intf->setSoundEffectChanMask(~0x3f); + + return true; +} + +void Player_Towns_v2::setMusicVolume(int vol) { + _imuse->setMusicVolume(vol); +} + +int Player_Towns_v2::getSoundStatus(int sound) const { + if (_soundOverride2[sound].type == 7) + return Player_Towns::getSoundStatus(sound); + return _imuse->getSoundStatus(sound); +} +void Player_Towns_v2::startSound(int sound) { + uint8 *ptr = _vm->getResourceAddress(rtSound, sound); + if (READ_BE_UINT32(ptr) == MKID_BE('TOWS')) { + _soundOverride2[sound].type = 7; + uint8 velo = _soundOverride2[sound].velo ? _soundOverride2[sound].velo - 1: (ptr[10] + ptr[11] + 1) >> 1; + uint8 pan = _soundOverride2[sound].pan ? _soundOverride2[sound].pan - 1 : 64; + uint8 pri = ptr[9]; + _soundOverride2[sound].velo = _soundOverride2[sound].pan = 0; + playPcmTrack(sound, ptr + 8, velo, pan, 0, pri); + } else if (READ_BE_UINT32(ptr) == MKID_BE('SBL ')) { + _soundOverride2[sound].type = 5; + playPcmTrackSBL(sound, ptr + 27); + } else { + _soundOverride2[sound].type = 3; + _imuse->startSound(sound); + } +} + +void Player_Towns_v2::stopSound(int sound) { + if (_soundOverride2[sound].type == 7) { + stopPcmTrack(sound); + } else { + _imuse->stopSound(sound); + } +} + +void Player_Towns_v2::stopAllSounds() { + stopPcmTrack(0); + _imuse->stopAllSounds(); +} + +int32 Player_Towns_v2::doCommand(int numargs, int args[]) { + int32 res = -1; + uint8 *ptr = 0; + + switch (args[0]) { + case 8: + startSound(args[1]); + res = 0; + break; + + case 9: + case 15: + stopSound(args[1]); + res = 0; + break; + + case 11: + stopPcmTrack(0); + break; + + case 13: + res = getSoundStatus(args[1]); + break; + + case 258: + if (_soundOverride2[args[1]].type == 0) { + ptr = _vm->getResourceAddress(rtSound, args[1]); + if (READ_BE_UINT32(ptr) == MKID_BE('TOWS')) + _soundOverride2[args[1]].type = 7; + } + if (_soundOverride2[args[1]].type == 7) { + _soundOverride2[args[1]].velo = args[2] + 1; + res = 0; + } + break; + + case 259: + if (_soundOverride2[args[1]].type == 0) { + ptr = _vm->getResourceAddress(rtSound, args[1]); + if (READ_BE_UINT32(ptr) == MKID_BE('TOWS')) + _soundOverride2[args[1]].type = 7; + } + if (_soundOverride2[args[1]].type == 7) { + int p = CLIP(args[2], -63, 63); + _soundOverride2[args[1]].pan = 64 - CLIP(args[2], -63, 63); + res = 0; + } + break; + + default: + break; + } + + if (res == -1) + return _imuse->doCommand(numargs, args); + + return res; +} + +void Player_Towns_v2::saveLoadWithSerializer(Serializer *ser) { + if (ser->getVersion() >= 83) + Player_Towns::saveLoadWithSerializer(ser); +} + +void Player_Towns_v2::playPcmTrackSBL(int sound, const uint8 *data) { + static const uint8 header[] = { + 0x54, 0x61, 0x6C, 0x6B, 0x69, 0x65, 0x20, 0x20, + 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x04, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00 + }; + + uint32 len = (READ_LE_UINT32(data) >> 8) - 2; + + int chan = getNextFreePcmChannel(0xffff, 0, 0x1000); + if (!chan) + return; + + memcpy(_sblData, header, 32); + WRITE_LE_UINT32(_sblData + 12, len); + + const uint8 *src = data + 6; + uint8 *dst = _sblData + 32; + for (int i = 0; i < len; i++) + *dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++; + + _intf->callback(37, 0x3f + chan, 60, 127, _sblData); + _pcmCurrentSound[chan].paused = 0; +} + +} // End of namespace Scumm diff --git a/engines/scumm/player_towns.h b/engines/scumm/player_towns.h index 3a36ea5daf..4635a436f0 100644 --- a/engines/scumm/player_towns.h +++ b/engines/scumm/player_towns.h @@ -27,24 +27,39 @@ #define SCUMM_PLAYER_TOWNS_H #include "scumm/scumm.h" -#include "scumm/music.h" +#include "scumm/imuse/imuse.h" #include "sound/softsynth/fmtowns_pc98/towns_euphony.h" namespace Scumm { -class Player_Towns { +class Player_Towns : public MusicEngine { public: - Player_Towns(ScummEngine *vm); + Player_Towns(ScummEngine *vm, bool isVersion2); virtual ~Player_Towns() {} + virtual bool init() = 0; + + void setSfxVolume(int vol); + + int getSoundStatus(int sound) const; + + virtual int32 doCommand(int numargs, int args[]) = 0; + virtual void saveLoadWithSerializer(Serializer *ser); virtual void restoreAfterLoad(); + // version 1 specific + virtual int getCurrentCdaSound() { return 0; } + virtual int getCurrentCdaVolume() { return 0; } + virtual void setVolumeCD(int left, int right) {} + virtual void setSoundVolume(int sound, int left, int right) {} + virtual void setSoundNote(int sound, int note) {} + protected: - void playPcmTrack(int sound, const uint8 *data, int velo = 0, int pan = 64, int note = 0); + void playPcmTrack(int sound, const uint8 *data, int velo = 0, int pan = 64, int note = 0, int priority = 0); void stopPcmTrack(int sound); - int getNextFreePcmChannel(int sound, int sfxChanRelIndex); + int getNextFreePcmChannel(int sound, int sfxChanRelIndex, int priority); struct PcmCurrentSound { uint16 index; @@ -63,24 +78,34 @@ protected: uint8 note; }; - uint8 _unkFlags; - SoundOvrParameters *_soundOverride; SoundOvrParameters _ovrCur; + struct SoundOvrParameters2 { + uint8 velo; + uint8 pan; + uint8 type; + }; + + SoundOvrParameters2 *_soundOverride2; + + uint8 _unkFlags; + TownsAudioInterface *_intf; ScummEngine *_vm; + + const int _numSoundMax; + const bool _v2; }; -class Player_Towns_v1 : public Player_Towns, public MusicEngine { +class Player_Towns_v1 : public Player_Towns { public: Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer); - virtual ~Player_Towns_v1(); + ~Player_Towns_v1(); bool init(); void setMusicVolume(int vol); - void setSfxVolume(int vol); void startSound(int sound); void stopSound(int sound); void stopAllSounds(); @@ -126,6 +151,32 @@ private: TownsEuphonyDriver *_driver; }; +class Player_Towns_v2 : public Player_Towns { +public: + Player_Towns_v2(ScummEngine *vm, IMuse *imuse, Audio::Mixer *mixer, bool disposeIMuse); + ~Player_Towns_v2(); + + bool init(); + + void setMusicVolume(int vol); + + int getSoundStatus(int sound) const; + void startSound(int sound); + void stopSound(int sound); + void stopAllSounds(); + + int32 doCommand(int numargs, int args[]); + + void saveLoadWithSerializer(Serializer *ser); + +private: + void playPcmTrackSBL(int sound, const uint8 *data); + + uint8 *_sblData; + IMuse *_imuse; + const bool _imuseDispose; +}; + } // End of namespace Scumm #endif diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index aaa79f38b6..d33ece7f6a 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -50,7 +50,7 @@ namespace Scumm { * only saves/loads those which are valid for the version of the savegame * which is being loaded/saved currently. */ -#define CURRENT_VER 82 +#define CURRENT_VER 83 /** * An auxillary macro, used to specify savegame versions. We use this instead diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index e0c044a52d..6d3808c653 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -1828,7 +1828,16 @@ void ScummEngine::setupMusic(int midi) { adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0); } - _musicEngine = _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver); + _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver); + + if (_game.platform == Common::kPlatformFMTowns) { + _musicEngine = _townsPlayer = new Player_Towns_v2(this, _imuse, _mixer, true); + if (!_townsPlayer->init()) + error("Failed to initialize FM-Towns audio driver"); + } else { + _musicEngine = _imuse; + } + if (_imuse) { _imuse->addSysexHandler (/*IMUSE_SYSEX_ID*/ 0x7D, @@ -1837,17 +1846,17 @@ void ScummEngine::setupMusic(int midi) { if (ConfMan.hasKey("tempo")) _imuse->property(IMuse::PROP_TEMPO_BASE, ConfMan.getInt("tempo")); // YM2162 driver can't handle midi->getPercussionChannel(), NULL shouldn't init MT-32/GM/GS - if ((midi != MDT_TOWNS) && (midi != MDT_NONE)) { + if (/*(midi != MDT_TOWNS) && (*/midi != MDT_NONE/*)*/) { _imuse->property(IMuse::PROP_NATIVE_MT32, _native_mt32); if (MidiDriver::getMusicType(dev) != MT_MT32) // MT-32 Emulation shouldn't be GM/GS initialized _imuse->property(IMuse::PROP_GS, _enable_gs); } - if (_game.heversion >= 60 || midi == MDT_TOWNS) { + if (_game.heversion >= 60 /*|| midi == MDT_TOWNS*/) { _imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1); _imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1); } - if (midi == MDT_TOWNS) - _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1); + /*if (midi == MDT_TOWNS) + _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);*/ } } } diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 53456be166..aef5cfbec7 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -81,7 +81,7 @@ class CharsetRenderer; class IMuse; class IMuseDigital; class MusicEngine; -class Player_Towns_v1; +class Player_Towns; class ScummEngine; class ScummDebugger; class Serializer; @@ -438,7 +438,7 @@ public: IMuse *_imuse; IMuseDigital *_imuseDigital; MusicEngine *_musicEngine; - Player_Towns_v1 *_townsPlayer; + Player_Towns *_townsPlayer; Sound *_sound; VerbSlot *_verbs; diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index eb157c3111..8fc6de8af9 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -245,7 +245,7 @@ void Sound::playSound(int soundID) { _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID); } // Support for sampled sound effects in Monkey Island 1 and 2 - else if (READ_BE_UINT32(ptr) == MKID_BE('SBL ')) { + else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKID_BE('SBL ')) { debugC(DEBUG_SOUND, "Using SBL sound effect"); // SBL resources essentially contain VOC sound data. @@ -314,7 +314,7 @@ void Sound::playSound(int soundID) { sound = (byte *)malloc(size); memcpy(sound, ptr + 6, size); stream = Audio::makeRawStream(sound, size, rate, Audio::FLAG_UNSIGNED); - _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID); + _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID); } else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKID_BE('SOUN')) { if (_vm->_game.version != 3) -- cgit v1.2.3