diff options
| -rw-r--r-- | engines/lastexpress/data/snd.cpp | 85 | ||||
| -rw-r--r-- | engines/lastexpress/data/snd.h | 7 | ||||
| -rw-r--r-- | engines/lastexpress/sound/entry.cpp | 55 | ||||
| -rw-r--r-- | engines/lastexpress/sound/entry.h | 24 | ||||
| -rw-r--r-- | engines/lastexpress/sound/queue.cpp | 10 | ||||
| -rw-r--r-- | engines/lastexpress/sound/sound.cpp | 4 | 
6 files changed, 103 insertions, 82 deletions
diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp index 4cff837193..7fb2c07706 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 23aae905ac..f6f2c5ec04 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 ea9b767ec8..9f7395bdd3 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 fa970cd097..938bbaf19a 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 5e8bf03b4a..8eef091f51 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 59e69613b4..b69121bbb5 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);  			}  		}  	}  | 
