diff options
author | Evgeny Grechnikov | 2018-10-16 23:01:26 +0300 |
---|---|---|
committer | Evgeny Grechnikov | 2018-10-16 23:01:26 +0300 |
commit | 29f6ce1d9a69663c9a040f5dd091bb07427d1aa7 (patch) | |
tree | 0cc5fc4ab0a994620c894414e4ba79740b70e46f /engines | |
parent | cc5d858169116c2758c4c3c0eb0cf983b9ccce21 (diff) | |
download | scummvm-rg350-29f6ce1d9a69663c9a040f5dd091bb07427d1aa7.tar.gz scummvm-rg350-29f6ce1d9a69663c9a040f5dd091bb07427d1aa7.tar.bz2 scummvm-rg350-29f6ce1d9a69663c9a040f5dd091bb07427d1aa7.zip |
LASTEXPRESS: support for delay-activated sounds
Not very obvious, but noticeable e.g. when knocking on harem doors.
I suppose this is the problem that wiki describes
as "improper triggering of actions on sound end".
Diffstat (limited to 'engines')
-rw-r--r-- | engines/lastexpress/sound/entry.cpp | 84 | ||||
-rw-r--r-- | engines/lastexpress/sound/entry.h | 20 | ||||
-rw-r--r-- | engines/lastexpress/sound/queue.cpp | 5 | ||||
-rw-r--r-- | engines/lastexpress/sound/sound.cpp | 28 | ||||
-rw-r--r-- | engines/lastexpress/sound/sound.h | 7 |
5 files changed, 80 insertions, 64 deletions
diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp index 945fc2318b..ea9b767ec8 100644 --- a/engines/lastexpress/sound/entry.cpp +++ b/engines/lastexpress/sound/entry.cpp @@ -55,16 +55,15 @@ SoundEntry::SoundEntry(LastExpressEngine *engine) : _engine(engine) { _field_34 = 0; _field_38 = 0; _field_3C = 0; - _variant = 0; + _volumeWithoutNIS = 0; _entity = kEntityPlayer; - _field_48 = 0; + _initTimeMS = 0; + _activateDelayMS = 0; _priority = 0; _subtitle = NULL; _soundStream = NULL; - - _queued = false; } SoundEntry::~SoundEntry() { @@ -123,27 +122,17 @@ void SoundEntry::play() { if (!_soundStream) _soundStream = new StreamedSound(); - // Compute current filter id - int32 filterId = _status & kSoundVolumeMask; - // TODO adjust status (based on stepIndex) - - if (_queued) { - _soundStream->setFilterId(filterId); - } else { - _stream->seek(0); - - // Load the stream and start playing - _soundStream->load(_stream, filterId); + _stream->seek(0); - _queued = true; - } + // Load the stream and start playing + _soundStream->load(_stream, _status & kSoundVolumeMask); } bool SoundEntry::isFinished() { if (!_stream) return true; - if (!_soundStream || !_queued) + if (!_soundStream) return false; // TODO check that all data has been queued @@ -254,8 +243,8 @@ void SoundEntry::update(uint val) { if (val) { if (getSoundQueue()->getFlag() & 32) { - _variant = val; - value2 = val * 2 + 1; + _volumeWithoutNIS = val; + value2 = val / 2 + 1; } _field_3C = value2; @@ -266,7 +255,7 @@ void SoundEntry::update(uint val) { } bool SoundEntry::updateSound() { - assert(_name2.size() <= 16); + assert(_name2.size() < 16); bool result; char sub[16]; @@ -275,15 +264,16 @@ bool SoundEntry::updateSound() { result = false; } else { if (_status & kSoundFlagDelayedActivate) { - if (_field_48 <= getSound()->getData2()) { - _status |= kSoundFlagPlayRequested; + // counter overflow is processed correctly + if (_engine->_system->getMillis() - _initTimeMS >= _activateDelayMS) { _status &= ~kSoundFlagDelayedActivate; - strcpy(sub, _name2.c_str()); + play(); - // FIXME: Rewrite and document expected behavior - int l = strlen(sub) + 1; - if (l - 1 > 4) - sub[l - (1 + 4)] = 0; + // drop .SND extension + strcpy(sub, _name2.c_str()); + int l = _name2.size(); + if (l > 4) + sub[l - 4] = 0; showSubtitle(sub); } } else { @@ -312,25 +302,33 @@ void SoundEntry::updateEntryFlag(SoundFlag flag) { else _status = flag + (_status & ~kSoundVolumeMask); } else { - _variant = 0; + _volumeWithoutNIS = 0; _status |= kSoundFlagMuteRequested; _status &= ~(kSoundFlagVolumeChanging | kSoundVolumeMask); } + if (_soundStream) + _soundStream->setFilterId(_status & kSoundVolumeMask); } -void SoundEntry::updateState() { +void SoundEntry::adjustVolumeIfNISPlaying() { if (getSoundQueue()->getFlag() & 32) { if (_type != kSoundType9 && _type != kSoundType7 && _type != kSoundType5) { - uint32 variant = _status & kSoundVolumeMask; + uint32 baseVolume = _status & kSoundVolumeMask; + uint32 actualVolume = baseVolume / 2 + 1; - _status &= ~kSoundVolumeMask; + assert((actualVolume & kSoundVolumeMask) == actualVolume); - _variant = variant; - _status |= variant * 2 + 1; + _volumeWithoutNIS = baseVolume; + _status &= ~kSoundVolumeMask; + _status |= actualVolume; } } +} - _status |= kSoundFlagPlayRequested; +void SoundEntry::initDelayedActivate(unsigned activateDelay) { + _initTimeMS = _engine->_system->getMillis(); + _activateDelayMS = activateDelay * 1000 / 15; + _status |= kSoundFlagDelayedActivate; } void SoundEntry::reset() { @@ -375,10 +373,18 @@ void SoundEntry::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsUint32LE(_field_38); // field_14; s.syncAsUint32LE(_entity); - uint32 delta = (uint32)_field_48 - getSound()->getData2(); - if (delta > 0x8000000u) // sanity check against overflow - delta = 0; - s.syncAsUint32LE(delta); + if (s.isLoading()) { + uint32 delta; + s.syncAsUint32LE(delta); + _initTimeMS = _engine->_system->getMillis(); + _activateDelayMS = delta * 1000 / 15; + } else { + uint32 deltaMS = _initTimeMS + _activateDelayMS - _engine->_system->getMillis(); + if (deltaMS > 0x8000000u) // sanity check against overflow + deltaMS = 0; + uint32 delta = deltaMS * 15 / 1000; + s.syncAsUint32LE(delta); + } s.syncAsUint32LE(_priority); diff --git a/engines/lastexpress/sound/entry.h b/engines/lastexpress/sound/entry.h index 4769d83d47..fa970cd097 100644 --- a/engines/lastexpress/sound/entry.h +++ b/engines/lastexpress/sound/entry.h @@ -54,7 +54,9 @@ uint32 {4} - ?? uint32 {4} - ?? uint32 {4} - ?? - uint32 {4} - ?? + uint32 {4} - base volume if NIS is playing + (the actual volume is reduced in half for non-NIS sounds; + this is used to restore the volume after NIS ends) uint32 {4} - entity uint32 {4} - ?? uint32 {4} - priority @@ -91,8 +93,10 @@ public: bool isFinished(); void update(uint val); bool updateSound(); - void updateState(); + void adjustVolumeIfNISPlaying(); void updateEntryFlag(SoundFlag flag); + // activateDelay is measured in main ticks, 15Hz timer + void initDelayedActivate(unsigned activateDelay); // Subtitles void showSubtitle(Common::String filename); @@ -101,10 +105,9 @@ public: void saveLoadWithSerializer(Common::Serializer &ser); // Accessors - void setStatus(uint32 status) { _status = status; } + void addStatusFlag(SoundFlag flag) { _status |= flag; } void setType(SoundType type) { _type = type; } void setEntity(EntityIndex entity) { _entity = entity; } - void setField48(int val) { _field_48 = val; } uint32 getStatus() { return _status; } SoundType getType() { return _type; } @@ -137,9 +140,13 @@ private: int _field_34; int _field_38; int _field_3C; - int _variant; + int _volumeWithoutNIS; EntityIndex _entity; - int _field_48; + // The original game uses one variable _activateTime = _initTime + _activateDelay + // and measures everything in sound ticks (30Hz timer). + // We use milliseconds and two variables to deal with possible overflow + // (probably paranoid, but nothing really complicated). + uint32 _initTimeMS, _activateDelayMS; uint32 _priority; Common::String _name1; //char[16]; Common::String _name2; //char[16]; @@ -147,7 +154,6 @@ private: SubtitleEntry *_subtitle; // Sound buffer & stream - bool _queued; StreamedSound *_soundStream; // the filtered sound stream void setType(SoundFlag flag); diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp index ce71536c1e..5e8bf03b4a 100644 --- a/engines/lastexpress/sound/queue.cpp +++ b/engines/lastexpress/sound/queue.cpp @@ -116,9 +116,6 @@ void SoundQueue::updateQueue() { it = _soundList.reverse_erase(it); continue; } - - // Queue the entry data, applying filtering - entry->play(); } // Original update the current entry, loading another set of samples to be decoded @@ -177,7 +174,7 @@ void SoundQueue::clearQueue() { ////////////////////////////////////////////////////////////////////////// void SoundQueue::clearStatus() { for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) - (*i)->setStatus((*i)->getStatus() | kSoundFlagCloseRequested); + (*i)->addStatusFlag(kSoundFlagCloseRequested); } ////////////////////////////////////////////////////////////////////////// diff --git a/engines/lastexpress/sound/sound.cpp b/engines/lastexpress/sound/sound.cpp index c8240e90c2..59e69613b4 100644 --- a/engines/lastexpress/sound/sound.cpp +++ b/engines/lastexpress/sound/sound.cpp @@ -135,7 +135,7 @@ SoundManager::~SoundManager() { ////////////////////////////////////////////////////////////////////////// // Sound-related functions ////////////////////////////////////////////////////////////////////////// -void SoundManager::playSound(EntityIndex entity, Common::String filename, SoundFlag flag, byte a4) { +void SoundManager::playSound(EntityIndex entity, Common::String filename, SoundFlag flag, byte activateDelay) { if (_queue->isBuffered(entity) && entity && entity < kEntityTrain) _queue->removeFromQueue(entity); @@ -145,20 +145,26 @@ void SoundManager::playSound(EntityIndex entity, Common::String filename, SoundF if (!filename.contains('.')) filename += ".SND"; - if (!playSoundWithSubtitles(filename, currentFlag, entity, a4)) + if (!playSoundWithSubtitles(filename, currentFlag, entity, activateDelay)) if (entity) getSavePoints()->push(kEntityPlayer, entity, kActionEndSound); } -bool SoundManager::playSoundWithSubtitles(Common::String filename, uint32 flag, EntityIndex entity, byte a4) { +bool SoundManager::playSoundWithSubtitles(Common::String filename, uint32 flag, EntityIndex entity, unsigned activateDelay) { SoundEntry *entry = new SoundEntry(_engine); entry->open(filename, (SoundFlag)flag, 30); entry->setEntity(entity); - if (a4) { - entry->setField48(_data2 + 2 * a4); - entry->setStatus(entry->getStatus() | kSoundFlagDelayedActivate); + // BUG: the original game skips adjustVolumeIfNISPlaying() for delayed-activate sounds. + // (the original code is structured in a slightly different way) + // Not sure whether it can be actually triggered, + // most delayed-activate sounds originate from user actions, + // all user actions are disabled while NIS is playing. + entry->adjustVolumeIfNISPlaying(); + + if (activateDelay) { + entry->initDelayedActivate(activateDelay); } else { // Get subtitles name uint32 size = filename.size(); @@ -166,7 +172,7 @@ bool SoundManager::playSoundWithSubtitles(Common::String filename, uint32 flag, filename.deleteLastChar(); entry->showSubtitle(filename); - entry->updateState(); + entry->play(); } // Add entry to sound list @@ -175,7 +181,7 @@ bool SoundManager::playSoundWithSubtitles(Common::String filename, uint32 flag, return (entry->getType() != kSoundTypeNone); } -void SoundManager::playSoundEvent(EntityIndex entity, byte action, byte a3) { +void SoundManager::playSoundEvent(EntityIndex entity, byte action, byte activateDelay) { int values[5]; if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car) @@ -193,14 +199,14 @@ void SoundManager::playSoundEvent(EntityIndex entity, byte action, byte a3) { if (_param3 > 7) { _data0 = (uint)_param3; - _data1 = _data2 + 2 * a3; + _data1 = _data2 + 2 * activateDelay; } break; } case 37: _data0 = 7; - _data1 = _data2 + 2 * a3; + _data1 = _data2 + 2 * activateDelay; break; case 150: @@ -298,7 +304,7 @@ void SoundManager::playSoundEvent(EntityIndex entity, byte action, byte a3) { } if (_action && flag) - playSoundWithSubtitles(Common::String::format("LIB%03d.SND", _action), flag, kEntityPlayer, a3); + playSoundWithSubtitles(Common::String::format("LIB%03d.SND", _action), flag, kEntityPlayer, activateDelay); } void SoundManager::playSteam(CityIndex index) { diff --git a/engines/lastexpress/sound/sound.h b/engines/lastexpress/sound/sound.h index f8e826a333..fad0e6dad9 100644 --- a/engines/lastexpress/sound/sound.h +++ b/engines/lastexpress/sound/sound.h @@ -38,9 +38,10 @@ public: ~SoundManager(); // Sound playing - void playSound(EntityIndex entity, Common::String filename, SoundFlag flag = kSoundVolumeEntityDefault, byte a4 = 0); - bool playSoundWithSubtitles(Common::String filename, uint32 flag, EntityIndex entity, byte a4 = 0); - void playSoundEvent(EntityIndex entity, byte action, byte a3 = 0); + // the original game uses byte in playSound but unsigned in playSoundWithSubtitles for activateDelay, no idea why + void playSound(EntityIndex entity, Common::String filename, SoundFlag flag = kSoundVolumeEntityDefault, byte activateDelay = 0); + bool playSoundWithSubtitles(Common::String filename, uint32 flag, EntityIndex entity, unsigned activateDelay = 0); + void playSoundEvent(EntityIndex entity, byte action, byte activateDelay = 0); void playDialog(EntityIndex entity, EntityIndex entityDialog, SoundFlag flag, byte a4); void playSteam(CityIndex index); void playFightSound(byte action, byte a4); |