aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorEvgeny Grechnikov2018-10-16 23:01:26 +0300
committerEvgeny Grechnikov2018-10-16 23:01:26 +0300
commit29f6ce1d9a69663c9a040f5dd091bb07427d1aa7 (patch)
tree0cc5fc4ab0a994620c894414e4ba79740b70e46f /engines
parentcc5d858169116c2758c4c3c0eb0cf983b9ccce21 (diff)
downloadscummvm-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.cpp84
-rw-r--r--engines/lastexpress/sound/entry.h20
-rw-r--r--engines/lastexpress/sound/queue.cpp5
-rw-r--r--engines/lastexpress/sound/sound.cpp28
-rw-r--r--engines/lastexpress/sound/sound.h7
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);