[Scummvm-git-logs] scummvm master -> 29f6ce1d9a69663c9a040f5dd091bb07427d1aa7

grechnik diamondaz at yandex.ru
Tue Oct 16 22:01:37 CEST 2018


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
29f6ce1d9a LASTEXPRESS: support for delay-activated sounds


Commit: 29f6ce1d9a69663c9a040f5dd091bb07427d1aa7
    https://github.com/scummvm/scummvm/commit/29f6ce1d9a69663c9a040f5dd091bb07427d1aa7
Author: Evgeny Grechnikov (diamondaz at yandex.ru)
Date: 2018-10-16T23:01:26+03:00

Commit Message:
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".

Changed paths:
    engines/lastexpress/sound/entry.cpp
    engines/lastexpress/sound/entry.h
    engines/lastexpress/sound/queue.cpp
    engines/lastexpress/sound/sound.cpp
    engines/lastexpress/sound/sound.h


diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp
index 945fc23..ea9b767 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 4769d83..fa970cd 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 ce71536..5e8bf03 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 c8240e9..59e6961 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 f8e826a..fad0e6d 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);





More information about the Scummvm-git-logs mailing list