[Scummvm-git-logs] scummvm master -> d561fd8a833e885e0d7d622c55f793569c1c1b74

grechnik diamondaz at yandex.ru
Tue Oct 16 22:58:24 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:
d561fd8a83 LASTEXPRESS: dynamic adjusting of sound volume


Commit: d561fd8a833e885e0d7d622c55f793569c1c1b74
    https://github.com/scummvm/scummvm/commit/d561fd8a833e885e0d7d622c55f793569c1c1b74
Author: Evgeny Grechnikov (diamondaz at yandex.ru)
Date: 2018-10-16T23:57:25+03:00

Commit Message:
LASTEXPRESS: dynamic adjusting of sound volume

Now it works just like in the original game,
including fading where it is applicable
(e.g. in a passengers list if closing the list while a sound is playing).

By the way, p2s sequence is known as http://oeis.org/A000265 ,
p1s is 4 - A007814, and p2s[i]/2**p1s[i] is just i/16.
It is time to get rid of these arrays.

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


diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp
index 4cff837..7fb2c07 100644
--- a/engines/lastexpress/data/snd.cpp
+++ b/engines/lastexpress/data/snd.cpp
@@ -342,9 +342,6 @@ static const int imaTable[1424] = {
 	-20479, -28671, -32767, -32767, -32767, -32767
 };
 
-static const int p1s[17] = { 0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4,  2, 4,  3, 4,  0 };
-static const int p2s[17] = { 0, 1, 1, 3, 1, 5, 3, 7, 1, 9, 5, 11, 3, 13, 7, 15, 1 };
-
 #pragma endregion
 
 // Last Express ADPCM is similar to MS IMA mono, but inverts its nibbles
@@ -352,12 +349,13 @@ static const int p2s[17] = { 0, 1, 1, 3, 1, 5, 3, 7, 1, 9, 5, 11, 3, 13, 7, 15,
 
 class LastExpress_ADPCMStream : public Audio::ADPCMStream {
 public:
-	LastExpress_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, uint32 blockSize, int32 filterId) :
+	LastExpress_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, uint32 blockSize, uint32 volume) :
 			Audio::ADPCMStream(stream, disposeAfterUse, size, 44100, 1, blockSize) {
-		_currentFilterId = -1;
-		_nextFilterId = filterId;
-		_stepAdjust1 = 0;
-		_stepAdjust2 = 0;
+		_currentVolume = 0;
+		_nextVolume = volume;
+		_smoothChangeTarget = volume;
+		_volumeHoldBlocks = 0;
+		_running = true;
 	}
 
 	int readBuffer(int16 *buffer, const int numSamples) {
@@ -369,24 +367,33 @@ public:
 
 		assert(numSamples % 2 == 0);
 
-		while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+		while (_running && samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
 			if (_blockPos[0] == _blockAlign) {
 				// read block header
 				_status.ima_ch[0].last = _stream->readSint16LE();
 				_status.ima_ch[0].stepIndex = _stream->readSint16LE() << 6;
 				_blockPos[0] = 4;
 
-				// Get current filter
-				_currentFilterId = _nextFilterId;
-				//_nextFilterId = -1; // FIXME: the filter id should be recomputed based on the sound entry status for each block
-
-				// No filter: skip decoding
-				if (_currentFilterId == -1)
-					break;
-
-				// Compute step adjustment
-				_stepAdjust1 = p1s[_currentFilterId];
-				_stepAdjust2 = p2s[_currentFilterId];
+				// Smooth transition, if requested
+				// the original game clears kSoundFlagVolumeChanging here if _nextVolume == _smoothChangeTarget
+				if (_nextVolume != _smoothChangeTarget) {
+					if (_volumeHoldBlocks > 3) {
+						if (_nextVolume < _smoothChangeTarget)
+							++_nextVolume;
+						else
+							--_nextVolume;
+						_volumeHoldBlocks = 0;
+						if (_nextVolume == 0) {
+							_running = false;
+							break;
+						}
+					} else {
+						_volumeHoldBlocks++;
+					}
+				}
+
+				// Get current volume
+				_currentVolume = _nextVolume;
 			}
 
 			for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
@@ -397,26 +404,28 @@ public:
 				idx = data >> 4;
 				step = stepTable[idx + _status.ima_ch[0].stepIndex / 4];
 				sample = CLIP<int>(imaTable[idx + _status.ima_ch[0].stepIndex / 4] + _status.ima_ch[0].last, -32767, 32767);
-				buffer[samples] = (_stepAdjust2 * sample) >> _stepAdjust1;
+				buffer[samples] = (sample * _currentVolume) >> 4;
 
 				// Second nibble
 				idx = data & 0xF;
 				_status.ima_ch[0].stepIndex = stepTable[idx + step / 4];
 				_status.ima_ch[0].last = CLIP(imaTable[idx + step / 4] + sample, -32767, 32767);
-				buffer[samples + 1] = (_stepAdjust2 * _status.ima_ch[0].last) >> _stepAdjust1;
+				buffer[samples + 1] = (_status.ima_ch[0].last * _currentVolume) >> 4;
 			}
 		}
 
 		return samples;
 	}
 
-	void setFilterId(int32 filterId) { _nextFilterId = filterId; }
+	void setVolume(uint32 newVolume) { _smoothChangeTarget = _nextVolume = newVolume; }
+	void setVolumeSmoothly(uint32 newVolume) { _smoothChangeTarget = newVolume; }
 
 private:
-	int32 _currentFilterId;
-	int32 _nextFilterId;    // the sound filter id, -1 for none
-	int32 _stepAdjust1;
-	int32 _stepAdjust2;
+	uint32 _currentVolume;
+	uint32 _nextVolume;
+	uint32 _smoothChangeTarget;
+	uint32 _volumeHoldBlocks; // smooth change of volume keeps volume on hold for 4 blocks = 133ms for every value; this is the counter
+	bool _running;
 };
 
 //////////////////////////////////////////////////////////////////////////
@@ -442,8 +451,8 @@ void SimpleSound::loadHeader(Common::SeekableReadStream *in) {
 	_blockSize = _size / _blocks;
 }
 
-LastExpress_ADPCMStream *SimpleSound::makeDecoder(Common::SeekableReadStream *in, uint32 size, int32 filterId) const {
-	return new LastExpress_ADPCMStream(in, DisposeAfterUse::YES, size, _blockSize, filterId);
+LastExpress_ADPCMStream *SimpleSound::makeDecoder(Common::SeekableReadStream *in, uint32 size, uint32 volume) const {
+	return new LastExpress_ADPCMStream(in, DisposeAfterUse::YES, size, _blockSize, volume);
 }
 
 void SimpleSound::play(Audio::AudioStream *as, DisposeAfterUse::Flag autofreeStream) {
@@ -461,7 +470,7 @@ StreamedSound::~StreamedSound() {
 	_as = NULL;
 }
 
-bool StreamedSound::load(Common::SeekableReadStream *stream, int32 filterId) {
+bool StreamedSound::load(Common::SeekableReadStream *stream, uint32 volume) {
 	if (!stream)
 		return false;
 
@@ -474,7 +483,7 @@ bool StreamedSound::load(Common::SeekableReadStream *stream, int32 filterId) {
 		delete _as;
 	}
 	// Start decoding the input stream
-	_as = makeDecoder(stream, _size, filterId);
+	_as = makeDecoder(stream, _size, volume);
 
 	// Start playing the decoded audio stream
 	play(_as, DisposeAfterUse::NO);
@@ -491,11 +500,18 @@ bool StreamedSound::isFinished() {
 	return !g_system->getMixer()->isSoundHandleActive(_handle);
 }
 
-void StreamedSound::setFilterId(int32 filterId) {
+void StreamedSound::setVolume(uint32 newVolume) {
+	if (!_as)
+		return;
+
+	_as->setVolume(newVolume);
+}
+
+void StreamedSound::setVolumeSmoothly(uint32 newVolume) {
 	if (!_as)
 		return;
 
-	_as->setFilterId(filterId);
+	_as->setVolumeSmoothly(newVolume);
 }
 
 //////////////////////////////////////////////////////////////////////////
@@ -531,8 +547,7 @@ void AppendableSound::queueBuffer(Common::SeekableReadStream *bufferIn) {
 
 	// Setup the ADPCM decoder
 	uint32 sizeIn = (uint32)bufferIn->size();
-	LastExpress_ADPCMStream *adpcm = makeDecoder(bufferIn, sizeIn);
-	adpcm->setFilterId(16);
+	LastExpress_ADPCMStream *adpcm = makeDecoder(bufferIn, sizeIn, kVolumeFull);
 
 	// Queue the stream
 	_as->queueAudioStream(adpcm);
diff --git a/engines/lastexpress/data/snd.h b/engines/lastexpress/data/snd.h
index 23aae90..f6f2c5e 100644
--- a/engines/lastexpress/data/snd.h
+++ b/engines/lastexpress/data/snd.h
@@ -61,7 +61,7 @@ public:
 
 protected:
 	void loadHeader(Common::SeekableReadStream *in);
-	LastExpress_ADPCMStream *makeDecoder(Common::SeekableReadStream *in, uint32 size, int32 filterId = -1) const;
+	LastExpress_ADPCMStream *makeDecoder(Common::SeekableReadStream *in, uint32 size, uint32 volume) const;
 	void play(Audio::AudioStream *as, DisposeAfterUse::Flag autofreeStream);
 
 	uint32 _size;   ///< data size
@@ -78,10 +78,11 @@ public:
 	StreamedSound();
 	~StreamedSound();
 
-	bool load(Common::SeekableReadStream *stream, int32 filterId = -1);
+	bool load(Common::SeekableReadStream *stream, uint32 volume);
 	virtual bool isFinished();
 
-	void setFilterId(int32 filterId);
+	void setVolume(uint32 newVolume);
+	void setVolumeSmoothly(uint32 newVolume);
 
 private:
 	LastExpress_ADPCMStream *_as;
diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp
index ea9b767..9f7395b 100644
--- a/engines/lastexpress/sound/entry.cpp
+++ b/engines/lastexpress/sound/entry.cpp
@@ -54,7 +54,6 @@ SoundEntry::SoundEntry(LastExpressEngine *engine) : _engine(engine) {
 
 	_field_34 = 0;
 	_field_38 = 0;
-	_field_3C = 0;
 	_volumeWithoutNIS = 0;
 	_entity = kEntityPlayer;
 	_initTimeMS = 0;
@@ -90,9 +89,9 @@ void SoundEntry::open(Common::String name, SoundFlag flag, int priority) {
 }
 
 void SoundEntry::close() {
-	_status |= kSoundFlagCloseRequested;
-
+	_status |= kSoundFlagClosed;
 	// Loop until ready
+	// _status |= kSoundFlagCloseRequested;
 	//while (!(_status & kSoundFlagClosed) && !(getSoundQueue()->getFlag() & 8) && (getSoundQueue()->getFlag() & 1))
 	//	;	// empty loop body
 
@@ -150,12 +149,12 @@ void SoundEntry::setType(SoundFlag flag) {
 	case kSoundTypeAmbient: {
 		SoundEntry *previous2 = getSoundQueue()->getEntry(kSoundType2);
 		if (previous2)
-			previous2->update(0);
+			previous2->fade();
 
 		SoundEntry *previous = getSoundQueue()->getEntry(kSoundType1);
 		if (previous) {
 			previous->setType(kSoundType2);
-			previous->update(0);
+			previous->fade();
 		}
 
 		_type = kSoundType1;
@@ -166,7 +165,7 @@ void SoundEntry::setType(SoundFlag flag) {
 		SoundEntry *previous = getSoundQueue()->getEntry(kSoundType3);
 		if (previous) {
 			previous->setType(kSoundType4);
-			previous->update(0);
+			previous->fade();
 		}
 
 		_type = kSoundType11;
@@ -230,28 +229,28 @@ void SoundEntry::loadStream(Common::String name) {
 		_stream = getArchive("DEFAULT.SND");
 
 	if (!_stream)
-		_status = kSoundFlagCloseRequested;
+		_status = kSoundFlagClosed;
 }
 
-void SoundEntry::update(uint val) {
+void SoundEntry::setVolumeSmoothly(SoundFlag newVolume) {
+	assert((newVolume & kSoundVolumeMask) == newVolume);
+
 	if (_status & kSoundFlagFading)
 		return;
 
-	int value2 = val;
-
-	_status |= kSoundFlagVolumeChanging;
-
-	if (val) {
-		if (getSoundQueue()->getFlag() & 32) {
-			_volumeWithoutNIS = val;
-			value2 = val / 2 + 1;
-		}
+	// the original game sets kSoundFlagVolumeChanging here
+	uint32 requestedVolume = (uint32)newVolume;
 
-		_field_3C = value2;
-	} else {
-		_field_3C = 0;
+	if (newVolume == kVolumeNone) {
 		_status |= kSoundFlagFading;
+	} else if (getSoundQueue()->getFlag() & 32) {
+		_volumeWithoutNIS = requestedVolume;
+		requestedVolume = requestedVolume / 2 + 1;
 	}
+
+	_status = (_status & ~kSoundVolumeMask) | requestedVolume;
+	if (_soundStream)
+		_soundStream->setVolumeSmoothly(requestedVolume);
 }
 
 bool SoundEntry::updateSound() {
@@ -281,7 +280,7 @@ bool SoundEntry::updateSound() {
 				if (!(_status & kSoundFlagFixedVolume)) {
 					if (_entity) {
 						if (_entity < 0x80) {
-							updateEntryFlag(getSound()->getSoundFlag(_entity));
+							setVolume(getSound()->getSoundFlag(_entity));
 						}
 					}
 				}
@@ -295,19 +294,21 @@ bool SoundEntry::updateSound() {
 	return result;
 }
 
-void SoundEntry::updateEntryFlag(SoundFlag flag) {
-	if (flag) {
+void SoundEntry::setVolume(SoundFlag newVolume) {
+	assert((newVolume & kSoundVolumeMask) == newVolume);
+
+	if (newVolume) {
 		if (getSoundQueue()->getFlag() & 0x20 && _type != kSoundType9 && _type != kSoundType7)
-			update(flag);
+			setVolumeSmoothly(newVolume);
 		else
-			_status = flag + (_status & ~kSoundVolumeMask);
+			_status = newVolume + (_status & ~kSoundVolumeMask);
 	} else {
 		_volumeWithoutNIS = 0;
 		_status |= kSoundFlagMuteRequested;
 		_status &= ~(kSoundFlagVolumeChanging | kSoundVolumeMask);
 	}
 	if (_soundStream)
-		_soundStream->setFilterId(_status & kSoundVolumeMask);
+		_soundStream->setVolume(_status & kSoundVolumeMask);
 }
 
 void SoundEntry::adjustVolumeIfNISPlaying() {
@@ -332,7 +333,7 @@ void SoundEntry::initDelayedActivate(unsigned activateDelay) {
 }
 
 void SoundEntry::reset() {
-	_status |= kSoundFlagCloseRequested;
+	_status |= kSoundFlagClosed;
 	_entity = kEntityPlayer;
 
 	if (_stream) {
diff --git a/engines/lastexpress/sound/entry.h b/engines/lastexpress/sound/entry.h
index fa970cd..938bbaf 100644
--- a/engines/lastexpress/sound/entry.h
+++ b/engines/lastexpress/sound/entry.h
@@ -29,10 +29,11 @@
 	    uint32 {4}      - type
 	    uint32 {4}      - blockCount
 	    uint32 {4}      - time
-	    uint32 {4}      - ??
-	    uint32 {4}      - ??
+	    uint32 {4}      - LastExpress_ADPCMStream::_volumeHoldBlocks
+	                      (useless since the target volume is not saved)
+	    uint32 {4}      - ?? [copy of a field below with no references]
 	    uint32 {4}      - entity
-	    uint32 {4}      - ??
+	    uint32 {4}      - activate delay in sound ticks (30Hz timer)
 	    uint32 {4}      - priority
 	    char {16}       - name 1
 	    char {16}       - name 2
@@ -51,14 +52,15 @@
 	    uint32 {4}      - ??
 	    uint32 {4}      - archive structure pointer
 	    uint32 {4}      - ??
-	    uint32 {4}      - ??
-	    uint32 {4}      - ??
-	    uint32 {4}      - ??
+	    uint32 {4}      - LastExpress_ADPCMStream::_volumeHoldBlocks
+	                      (used for smooth change of volume)
+	    uint32 {4}      - ?? [no references except for save/load]
+	    uint32 {4}      - target value for smooth change of volume
 	    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}      - activate time in sound ticks (30Hz timer)
 	    uint32 {4}      - priority
 	    char {16}       - name 1
 	    char {16}       - name 2
@@ -91,10 +93,13 @@ public:
 	void play();
 	void reset();
 	bool isFinished();
-	void update(uint val);
+	void setVolumeSmoothly(SoundFlag newVolume);
+	// setVolumeSmoothly() treats kVolumeNone in a special way;
+	// fade() terminates the stream after the transition
+	void fade() { setVolumeSmoothly(kVolumeNone); }
 	bool updateSound();
 	void adjustVolumeIfNISPlaying();
-	void updateEntryFlag(SoundFlag flag);
+	void setVolume(SoundFlag newVolume);
 	// activateDelay is measured in main ticks, 15Hz timer
 	void initDelayedActivate(unsigned activateDelay);
 
@@ -139,7 +144,6 @@ private:
 	//int _archive;
 	int _field_34;
 	int _field_38;
-	int _field_3C;
 	int _volumeWithoutNIS;
 	EntityIndex _entity;
 	// The original game uses one variable _activateTime = _initTime + _activateDelay
diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp
index 5e8bf03..8eef091 100644
--- a/engines/lastexpress/sound/queue.cpp
+++ b/engines/lastexpress/sound/queue.cpp
@@ -87,7 +87,7 @@ void SoundQueue::updateQueue() {
 			getSound()->playLoopingSound(0x45);
 		} else {
 			if (getSound()->getData1() && getSound()->getData2() >= getSound()->getData1()) {
-				entry->update(getSound()->getData0());
+				entry->setVolumeSmoothly((SoundFlag)getSound()->getData0());
 				getSound()->setData1(0);
 			}
 		}
@@ -174,7 +174,7 @@ void SoundQueue::clearQueue() {
 //////////////////////////////////////////////////////////////////////////
 void SoundQueue::clearStatus() {
 	for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i)
-		(*i)->addStatusFlag(kSoundFlagCloseRequested);
+		(*i)->addStatusFlag(kSoundFlagClosed);
 }
 
 //////////////////////////////////////////////////////////////////////////
@@ -189,7 +189,7 @@ void SoundQueue::setupEntry(SoundType type, EntityIndex index) {
 void SoundQueue::processEntry(EntityIndex entity) {
 	SoundEntry *entry = getEntry(entity);
 	if (entry) {
-		entry->update(0);
+		entry->fade();
 		entry->setEntity(kEntityPlayer);
 	}
 }
@@ -197,13 +197,13 @@ void SoundQueue::processEntry(EntityIndex entity) {
 void SoundQueue::processEntry(SoundType type) {
 	SoundEntry *entry = getEntry(type);
 	if (entry)
-		entry->update(0);
+		entry->fade();
 }
 
 void SoundQueue::processEntry(Common::String filename) {
 	SoundEntry *entry = getEntry(filename);
 	if (entry) {
-		entry->update(0);
+		entry->fade();
 		entry->setEntity(kEntityPlayer);
 	}
 }
diff --git a/engines/lastexpress/sound/sound.cpp b/engines/lastexpress/sound/sound.cpp
index 59e6961..b69121b 100644
--- a/engines/lastexpress/sound/sound.cpp
+++ b/engines/lastexpress/sound/sound.cpp
@@ -1380,11 +1380,11 @@ void SoundManager::playLoopingSound(int param) {
 				playSoundWithSubtitles(tmp, kSoundTypeAmbient | kSoundFlagLooped | kVolume1, kEntitySteam);
 
 				if (entry)
-					entry->update(0);
+					entry->fade();
 
 				SoundEntry *entry1 = _queue->getEntry(kSoundType1);
 				if (entry1)
-					entry1->update(7);
+					entry1->setVolumeSmoothly(kVolume7);
 			}
 		}
 	}





More information about the Scummvm-git-logs mailing list