aboutsummaryrefslogtreecommitdiff
path: root/engines/lastexpress
diff options
context:
space:
mode:
authorEvgeny Grechnikov2018-10-16 23:57:04 +0300
committerEvgeny Grechnikov2018-10-16 23:57:25 +0300
commitd561fd8a833e885e0d7d622c55f793569c1c1b74 (patch)
tree40702f83cdb39636f5554c49b45fb641d41e2f3f /engines/lastexpress
parent84f948de917a75d3f0f1c4a42155a24ed1d1ca07 (diff)
downloadscummvm-rg350-d561fd8a833e885e0d7d622c55f793569c1c1b74.tar.gz
scummvm-rg350-d561fd8a833e885e0d7d622c55f793569c1c1b74.tar.bz2
scummvm-rg350-d561fd8a833e885e0d7d622c55f793569c1c1b74.zip
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.
Diffstat (limited to 'engines/lastexpress')
-rw-r--r--engines/lastexpress/data/snd.cpp85
-rw-r--r--engines/lastexpress/data/snd.h7
-rw-r--r--engines/lastexpress/sound/entry.cpp55
-rw-r--r--engines/lastexpress/sound/entry.h24
-rw-r--r--engines/lastexpress/sound/queue.cpp10
-rw-r--r--engines/lastexpress/sound/sound.cpp4
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);
}
}
}