Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "lastexpress/lastexpress.h"
#include "audio/decoders/raw.h"
namespace LastExpress {
void SoundManager::soundDriverInit() {
_stream = Audio::makeQueuingAudioStream(22050, _engine->_mixer->getOutputStereo());
memset(_soundMixBuffer, 0, 1470 * sizeof(int16));
_mixer->playStream(
Audio::Mixer::kPlainSoundType,
&_channelHandle,
_stream,
-1,
Audio::Mixer::kMaxChannelVolume
);
// The following is a way to adapt the amount of buffers to the current device capabilities.
// Re-adapted from my own code found within the Digital iMUSE subsystem from the SCUMM engine.
_maxQueuedStreams = (uint32)ceil((_mixer->getOutputBufSize() / 1470) / ((float)_mixer->getOutputRate() / 44100));
if (_mixer->getOutputRate() % 44100) {
_maxQueuedStreams++;
}
_maxQueuedStreams = MAX<uint32>(4, _maxQueuedStreams);
}
void SoundManager::soundDriverCopyBuffersToDevice() {
_sound30HzCounter++;
while (_stream->numQueuedStreams() < _maxQueuedStreams) {
byte *ptr = (byte *)malloc(1470 * sizeof(int16));
assert(ptr);
mixEngine();
memcpy(ptr, _soundMixBuffer, 1470 * sizeof(int16));
byte flags = Audio::FLAG_16BITS;
if (_mixer->getOutputStereo()) {
flags |= Audio::FLAG_STEREO;
}
#ifdef SCUMM_LITTLE_ENDIAN
flags |= Audio::FLAG_LITTLE_ENDIAN;
#endif
_stream->queueBuffer(ptr, 1470 * sizeof(int16), DisposeAfterUse::YES, flags);
}
}
int SoundManager::soundDriverGetVolume() {
// I know, this thing could have been a look-up table :-)
// Still, I want this to be completely clear and transparent.
int scummVMVolume = _mixer->getChannelVolume(_channelHandle);
// Convert driver volume (0-255) to DirectSound format (-10000 to 0)
double dsVolume;
if (scummVMVolume == 0) {
dsVolume = -10000; // Silence
} else {
// Convert from linear scale to dB
dsVolume = (2000 * log10((double)scummVMVolume / 255.0));
}
// Convert DirectSound scale to game scale (0-7)
int engineVolume;
if (dsVolume <= -3000) {
engineVolume = 0;
} else {
engineVolume = (int)round((7 * (dsVolume + 3000)) / 3000);
}
return engineVolume;
}
void SoundManager::soundDriverSetVolume(int volume) {
// Convert from game scale (0-7) to DirectSound scale (-10000 to 0)
int32 dsVolume;
if (volume == 0) {
dsVolume = -10000; // Silence
} else {
dsVolume = 3000 * volume / 7 - 3000;
}
// Convert DirectSound volume to ScummVM volume (0-255)
int scummVMVolume = (int)round(pow(10, dsVolume / 2000.0) * 255.0);
assert(scummVMVolume >= 0 && scummVMVolume < 256);
_mixer->setChannelVolume(_channelHandle, (byte)scummVMVolume);
}
int32 SoundManager::getSoundDriverTicks() {
Common::StackLock lock(*_engine->_soundMutex);
return _soundDriverTicks;
}
void SoundManager::setSoundDriverTicks(int32 value) {
Common::StackLock lock(*_engine->_soundMutex);
_soundDriverTicks = value;
}
int32 SoundManager::getSoundDriver30HzCounter() {
Common::StackLock lock(*_engine->_soundMutex);
return _sound30HzCounter;
}
int32 SoundManager::getSoundDriverFlags() {
Common::StackLock lock(*_engine->_soundMutex);
return _soundDriverFlags;
}
void SoundManager::addSoundDriverFlags(int32 flags) {
Common::StackLock lock(*_engine->_soundMutex);
_soundDriverFlags |= flags;
}
void SoundManager::removeSoundDriverFlags(int32 flags) {
Common::StackLock lock(*_engine->_soundMutex);
_soundDriverFlags &= ~flags;
}
bool SoundManager::isCopyingDataToSoundDriver() {
Common::StackLock lock(*_engine->_soundMutex);
return _copyingDataToSoundDriver;
}
} // End of namespace LastExpress

View File

@@ -0,0 +1,729 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "lastexpress/sound/sound.h"
#include "lastexpress/helpers.h"
#include "lastexpress/lastexpress.h"
namespace LastExpress {
static int32 adpcmStepSizeTable[] = {
0, 0, 0, 0, 128, 256, 384, 512, 0, 0, 0, 0, 128, 256,
384, 512, 0, 0, 0, 0, 192, 320, 448, 576, 0, 0, 0, 0,
192, 320, 448, 576, 64, 64, 64, 64, 256, 384, 512, 640,
64, 64, 64, 64, 256, 384, 512, 640, 128, 128, 128, 128,
320, 448, 576, 704, 128, 128, 128, 128, 320, 448, 576,
704, 192, 192, 192, 192, 384, 512, 640, 768, 192, 192,
192, 192, 384, 512, 640, 768, 256, 256, 256, 256, 448,
576, 704, 832, 256, 256, 256, 256, 448, 576, 704, 832,
320, 320, 320, 320, 512, 640, 768, 896, 320, 320, 320,
320, 512, 640, 768, 896, 384, 384, 384, 384, 576, 704,
832, 960, 384, 384, 384, 384, 576, 704, 832, 960, 448,
448, 448, 448, 640, 768, 896, 1024, 448, 448, 448, 448,
640, 768, 896, 1024, 512, 512, 512, 512, 704, 832, 960,
1088, 512, 512, 512, 512, 704, 832, 960, 1088, 576,
576, 576, 576, 768, 896, 1024, 1152, 576, 576, 576,
576, 768, 896, 1024, 1152, 640, 640, 640, 640, 832,
960, 1088, 1216, 640, 640, 640, 640, 832, 960, 1088,
1216, 704, 704, 704, 704, 896, 1024, 1152, 1280, 704,
704, 704, 704, 896, 1024, 1152, 1280, 768, 768, 768,
768, 960, 1088, 1216, 1344, 768, 768, 768, 768, 960,
1088, 1216, 1344, 832, 832, 832, 832, 1024, 1152, 1280,
1408, 832, 832, 832, 832, 1024, 1152, 1280, 1408, 896,
896, 896, 896, 1088, 1216, 1344, 1472, 896, 896, 896,
896, 1088, 1216, 1344, 1472, 960, 960, 960, 960, 1152,
1280, 1408, 1536, 960, 960, 960, 960, 1152, 1280, 1408,
1536, 1024, 1024, 1024, 1024, 1216, 1344, 1472, 1600,
1024, 1024, 1024, 1024, 1216, 1344, 1472, 1600, 1088,
1088, 1088, 1088, 1280, 1408, 1536, 1664, 1088, 1088,
1088, 1088, 1280, 1408, 1536, 1664, 1152, 1152, 1152,
1152, 1344, 1472, 1600, 1728, 1152, 1152, 1152, 1152,
1344, 1472, 1600, 1728, 1216, 1216, 1216, 1216, 1408,
1536, 1664, 1792, 1216, 1216, 1216, 1216, 1408, 1536,
1664, 1792, 1280, 1280, 1280, 1280, 1472, 1600, 1728,
1856, 1280, 1280, 1280, 1280, 1472, 1600, 1728, 1856,
1344, 1344, 1344, 1344, 1536, 1664, 1792, 1920, 1344,
1344, 1344, 1344, 1536, 1664, 1792, 1920, 1408, 1408,
1408, 1408, 1600, 1728, 1856, 1984, 1408, 1408, 1408,
1408, 1600, 1728, 1856, 1984, 1472, 1472, 1472, 1472,
1664, 1792, 1920, 2048, 1472, 1472, 1472, 1472, 1664,
1792, 1920, 2048, 1536, 1536, 1536, 1536, 1728, 1856,
1984, 2112, 1536, 1536, 1536, 1536, 1728, 1856, 1984,
2112, 1600, 1600, 1600, 1600, 1792, 1920, 2048, 2176,
1600, 1600, 1600, 1600, 1792, 1920, 2048, 2176, 1664,
1664, 1664, 1664, 1856, 1984, 2112, 2240, 1664, 1664,
1664, 1664, 1856, 1984, 2112, 2240, 1728, 1728, 1728,
1728, 1920, 2048, 2176, 2304, 1728, 1728, 1728, 1728,
1920, 2048, 2176, 2304, 1792, 1792, 1792, 1792, 1984,
2112, 2240, 2368, 1792, 1792, 1792, 1792, 1984, 2112,
2240, 2368, 1856, 1856, 1856, 1856, 2048, 2176, 2304,
2432, 1856, 1856, 1856, 1856, 2048, 2176, 2304, 2432,
1920, 1920, 1920, 1920, 2112, 2240, 2368, 2496, 1920,
1920, 1920, 1920, 2112, 2240, 2368, 2496, 1984, 1984,
1984, 1984, 2176, 2304, 2432, 2560, 1984, 1984, 1984,
1984, 2176, 2304, 2432, 2560, 2048, 2048, 2048, 2048,
2240, 2368, 2496, 2624, 2048, 2048, 2048, 2048, 2240,
2368, 2496, 2624, 2112, 2112, 2112, 2112, 2304, 2432,
2560, 2688, 2112, 2112, 2112, 2112, 2304, 2432, 2560,
2688, 2176, 2176, 2176, 2176, 2368, 2496, 2624, 2752,
2176, 2176, 2176, 2176, 2368, 2496, 2624, 2752, 2240,
2240, 2240, 2240, 2432, 2560, 2688, 2816, 2240, 2240,
2240, 2240, 2432, 2560, 2688, 2816, 2304, 2304, 2304,
2304, 2496, 2624, 2752, 2880, 2304, 2304, 2304, 2304,
2496, 2624, 2752, 2880, 2368, 2368, 2368, 2368, 2560,
2688, 2816, 2944, 2368, 2368, 2368, 2368, 2560, 2688,
2816, 2944, 2432, 2432, 2432, 2432, 2624, 2752, 2880,
3008, 2432, 2432, 2432, 2432, 2624, 2752, 2880, 3008,
2496, 2496, 2496, 2496, 2688, 2816, 2944, 3072, 2496,
2496, 2496, 2496, 2688, 2816, 2944, 3072, 2560, 2560,
2560, 2560, 2752, 2880, 3008, 3136, 2560, 2560, 2560,
2560, 2752, 2880, 3008, 3136, 2624, 2624, 2624, 2624,
2816, 2944, 3072, 3200, 2624, 2624, 2624, 2624, 2816,
2944, 3072, 3200, 2688, 2688, 2688, 2688, 2880, 3008,
3136, 3264, 2688, 2688, 2688, 2688, 2880, 3008, 3136,
3264, 2752, 2752, 2752, 2752, 2944, 3072, 3200, 3328,
2752, 2752, 2752, 2752, 2944, 3072, 3200, 3328, 2816,
2816, 2816, 2816, 3008, 3136, 3264, 3392, 2816, 2816,
2816, 2816, 3008, 3136, 3264, 3392, 2880, 2880, 2880,
2880, 3072, 3200, 3328, 3456, 2880, 2880, 2880, 2880,
3072, 3200, 3328, 3456, 2944, 2944, 2944, 2944, 3136,
3264, 3392, 3520, 2944, 2944, 2944, 2944, 3136, 3264,
3392, 3520, 3008, 3008, 3008, 3008, 3200, 3328, 3456,
3584, 3008, 3008, 3008, 3008, 3200, 3328, 3456, 3584,
3072, 3072, 3072, 3072, 3264, 3392, 3520, 3648, 3072,
3072, 3072, 3072, 3264, 3392, 3520, 3648, 3136, 3136,
3136, 3136, 3328, 3456, 3584, 3712, 3136, 3136, 3136,
3136, 3328, 3456, 3584, 3712, 3200, 3200, 3200, 3200,
3392, 3520, 3648, 3776, 3200, 3200, 3200, 3200, 3392,
3520, 3648, 3776, 3264, 3264, 3264, 3264, 3456, 3584,
3712, 3840, 3264, 3264, 3264, 3264, 3456, 3584, 3712,
3840, 3328, 3328, 3328, 3328, 3520, 3648, 3776, 3904,
3328, 3328, 3328, 3328, 3520, 3648, 3776, 3904, 3392,
3392, 3392, 3392, 3584, 3712, 3840, 3968, 3392, 3392,
3392, 3392, 3584, 3712, 3840, 3968, 3456, 3456, 3456,
3456, 3648, 3776, 3904, 4032, 3456, 3456, 3456, 3456,
3648, 3776, 3904, 4032, 3520, 3520, 3520, 3520, 3712,
3840, 3968, 4096, 3520, 3520, 3520, 3520, 3712, 3840,
3968, 4096, 3584, 3584, 3584, 3584, 3776, 3904, 4032,
4160, 3584, 3584, 3584, 3584, 3776, 3904, 4032, 4160,
3648, 3648, 3648, 3648, 3840, 3968, 4096, 4224, 3648,
3648, 3648, 3648, 3840, 3968, 4096, 4224, 3712, 3712,
3712, 3712, 3904, 4032, 4160, 4288, 3712, 3712, 3712,
3712, 3904, 4032, 4160, 4288, 3776, 3776, 3776, 3776,
3968, 4096, 4224, 4352, 3776, 3776, 3776, 3776, 3968,
4096, 4224, 4352, 3840, 3840, 3840, 3840, 4032, 4160,
4288, 4416, 3840, 3840, 3840, 3840, 4032, 4160, 4288,
4416, 3904, 3904, 3904, 3904, 4096, 4224, 4352, 4480,
3904, 3904, 3904, 3904, 4096, 4224, 4352, 4480, 3968,
3968, 3968, 3968, 4160, 4288, 4416, 4544, 3968, 3968,
3968, 3968, 4160, 4288, 4416, 4544, 4032, 4032, 4032,
4032, 4224, 4352, 4480, 4608, 4032, 4032, 4032, 4032,
4224, 4352, 4480, 4608, 4096, 4096, 4096, 4096, 4288,
4416, 4544, 4672, 4096, 4096, 4096, 4096, 4288, 4416,
4544, 4672, 4160, 4160, 4160, 4160, 4352, 4480, 4608,
4736, 4160, 4160, 4160, 4160, 4352, 4480, 4608, 4736,
4224, 4224, 4224, 4224, 4416, 4544, 4672, 4800, 4224,
4224, 4224, 4224, 4416, 4544, 4672, 4800, 4288, 4288,
4288, 4288, 4480, 4608, 4736, 4864, 4288, 4288, 4288,
4288, 4480, 4608, 4736, 4864, 4352, 4352, 4352, 4352,
4544, 4672, 4800, 4928, 4352, 4352, 4352, 4352, 4544,
4672, 4800, 4928, 4416, 4416, 4416, 4416, 4608, 4736,
4864, 4992, 4416, 4416, 4416, 4416, 4608, 4736, 4864,
4992, 4480, 4480, 4480, 4480, 4672, 4800, 4928, 5056,
4480, 4480, 4480, 4480, 4672, 4800, 4928, 5056, 4544,
4544, 4544, 4544, 4736, 4864, 4992, 5120, 4544, 4544,
4544, 4544, 4736, 4864, 4992, 5120, 4608, 4608, 4608,
4608, 4800, 4928, 5056, 5184, 4608, 4608, 4608, 4608,
4800, 4928, 5056, 5184, 4672, 4672, 4672, 4672, 4864,
4992, 5120, 5248, 4672, 4672, 4672, 4672, 4864, 4992,
5120, 5248, 4736, 4736, 4736, 4736, 4928, 5056, 5184,
5312, 4736, 4736, 4736, 4736, 4928, 5056, 5184, 5312,
4800, 4800, 4800, 4800, 4992, 5120, 5248, 5376, 4800,
4800, 4800, 4800, 4992, 5120, 5248, 5376, 4864, 4864,
4864, 4864, 5056, 5184, 5312, 5440, 4864, 4864, 4864,
4864, 5056, 5184, 5312, 5440, 4928, 4928, 4928, 4928,
5120, 5248, 5376, 5504, 4928, 4928, 4928, 4928, 5120,
5248, 5376, 5504, 4992, 4992, 4992, 4992, 5184, 5312,
5440, 5568, 4992, 4992, 4992, 4992, 5184, 5312, 5440,
5568, 5056, 5056, 5056, 5056, 5248, 5376, 5504, 5632,
5056, 5056, 5056, 5056, 5248, 5376, 5504, 5632, 5120,
5120, 5120, 5120, 5312, 5440, 5568, 5632, 5120, 5120,
5120, 5120, 5312, 5440, 5568, 5632, 5184, 5184, 5184,
5184, 5376, 5504, 5632, 5632, 5184, 5184, 5184, 5184,
5376, 5504, 5632, 5632, 5248, 5248, 5248, 5248, 5440,
5568, 5632, 5632, 5248, 5248, 5248, 5248, 5440, 5568,
5632, 5632, 5312, 5312, 5312, 5312, 5504, 5632, 5632,
5632, 5312, 5312, 5312, 5312, 5504, 5632, 5632, 5632,
5376, 5376, 5376, 5376, 5568, 5632, 5632, 5632, 5376,
5376, 5376, 5376, 5568, 5632, 5632, 5632, 5440, 5440,
5440, 5440, 5632, 5632, 5632, 5632, 5440, 5440, 5440,
5440, 5632, 5632, 5632, 5632, 5504, 5504, 5504, 5504,
5632, 5632, 5632, 5632, 5504, 5504, 5504, 5504, 5632,
5632, 5632, 5632, 5568, 5568, 5568, 5568, 5632, 5632,
5632, 5632, 5568, 5568, 5568, 5568, 5632, 5632, 5632,
5632
};
static int32 adpcmDeltaTable[] = {
0, 2, 4, 6, 7, 9, 11, 13, 0, -2, -4, -6, -7, -9, -11,
-13, 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9,
-11, -13, -15, 1, 3, 5, 7, 10, 12, 14, 16, -1, -3, -5,
-7, -10, -12, -14, -16, 1, 3, 6, 8, 11, 13, 16, 18,
-1, -3, -6, -8, -11, -13, -16, -18, 1, 4, 6, 9, 12,
15, 17, 20, -1, -4, -6, -9, -12, -15, -17, -20, 1, 4,
7, 10, 13, 16, 19, 22, -1, -4, -7, -10, -13, -16, -19,
-22, 1, 4, 8, 11, 14, 17, 21, 24, -1, -4, -8, -11, -14,
-17, -21, -24, 1, 5, 8, 12, 15, 19, 22, 26, -1, -5,
-8, -12, -15, -19, -22, -26, 2, 6, 10, 14, 18, 22, 26,
30, -2, -6, -10, -14, -18, -22, -26, -30, 2, 6, 10,
14, 19, 23, 27, 31, -2, -6, -10, -14, -19, -23, -27,
-31, 2, 7, 11, 16, 21, 26, 30, 35, -2, -7, -11, -16,
-21, -26, -30, -35, 2, 7, 13, 18, 23, 28, 34, 39, -2,
-7, -13, -18, -23, -28, -34, -39, 2, 8, 14, 20, 25,
31, 37, 43, -2, -8, -14, -20, -25, -31, -37, -43, 3,
9, 15, 21, 28, 34, 40, 46, -3, -9, -15, -21, -28, -34,
-40, -46, 3, 10, 17, 24, 31, 38, 45, 52, -3, -10, -17,
-24, -31, -38, -45, -52, 3, 11, 19, 27, 34, 42, 50,
58, -3, -11, -19, -27, -34, -42, -50, -58, 4, 12, 21,
29, 38, 46, 55, 63, -4, -12, -21, -29, -38, -46, -55,
-63, 4, 13, 23, 32, 41, 50, 60, 69, -4, -13, -23, -32,
-41, -50, -60, -69, 5, 15, 25, 35, 46, 56, 66, 76, -5,
-15, -25, -35, -46, -56, -66, -76, 5, 16, 28, 39, 50,
61, 73, 84, -5, -16, -28, -39, -50, -61, -73, -84, 6,
18, 31, 43, 56, 68, 81, 93, -6, -18, -31, -43, -56,
-68, -81, -93, 6, 20, 34, 48, 61, 75, 89, 103, -6, -20,
-34, -48, -61, -75, -89, -103, 7, 22, 37, 52, 67, 82,
97, 112, -7, -22, -37, -52, -67, -82, -97, -112, 8,
24, 41, 57, 74, 90, 107, 123, -8, -24, -41, -57, -74,
-90, -107, -123, 9, 27, 45, 63, 82, 100, 118, 136, -9,
-27, -45, -63, -82, -100, -118, -136, 10, 30, 50, 70,
90, 110, 130, 150, -10, -30, -50, -70, -90, -110, -130,
-150, 11, 33, 55, 77, 99, 121, 143, 165, -11, -33, -55,
-77, -99, -121, -143, -165, 12, 36, 60, 84, 109, 133,
157, 181, -12, -36, -60, -84, -109, -133, -157, -181,
13, 40, 66, 93, 120, 147, 173, 200, -13, -40, -66, -93,
-120, -147, -173, -200, 14, 44, 73, 103, 132, 162, 191,
221, -14, -44, -73, -103, -132, -162, -191, -221, 16,
48, 81, 113, 146, 178, 211, 243, -16, -48, -81, -113,
-146, -178, -211, -243, 17, 53, 89, 125, 160, 196, 232,
268, -17, -53, -89, -125, -160, -196, -232, -268, 19,
58, 98, 137, 176, 215, 255, 294, -19, -58, -98, -137,
-176, -215, -255, -294, 21, 64, 108, 151, 194, 237,
281, 324, -21, -64, -108, -151, -194, -237, -281, -324,
23, 71, 118, 166, 213, 261, 308, 356, -23, -71, -118,
-166, -213, -261, -308, -356, 26, 78, 130, 182, 235,
287, 339, 391, -26, -78, -130, -182, -235, -287, -339,
-391, 28, 86, 143, 201, 258, 316, 373, 431, -28, -86,
-143, -201, -258, -316, -373, -431, 31, 94, 158, 221,
284, 347, 411, 474, -31, -94, -158, -221, -284, -347,
-411, -474, 34, 104, 174, 244, 313, 383, 453, 523, -34,
-104, -174, -244, -313, -383, -453, -523, 38, 115, 191,
268, 345, 422, 498, 575, -38, -115, -191, -268, -345,
-422, -498, -575, 42, 126, 210, 294, 379, 463, 547,
631, -42, -126, -210, -294, -379, -463, -547, -631,
46, 139, 231, 324, 417, 510, 602, 695, -46, -139, -231,
-324, -417, -510, -602, -695, 51, 153, 255, 357, 459,
561, 663, 765, -51, -153, -255, -357, -459, -561, -663,
-765, 56, 168, 280, 392, 505, 617, 729, 841, -56, -168,
-280, -392, -505, -617, -729, -841, 61, 185, 308, 432,
555, 679, 802, 926, -61, -185, -308, -432, -555, -679,
-802, -926, 68, 204, 340, 476, 612, 748, 884, 1020,
-68, -204, -340, -476, -612, -748, -884, -1020, 74,
224, 373, 523, 672, 822, 971, 1121, -74, -224, -373,
-523, -672, -822, -971, -1121, 82, 246, 411, 575, 740,
904, 1069, 1233, -82, -246, -411, -575, -740, -904,
-1069, -1233, 90, 271, 452, 633, 814, 995, 1176, 1357,
-90, -271, -452, -633, -814, -995, -1176, -1357, 99,
298, 497, 696, 895, 1094, 1293, 1492, -99, -298, -497,
-696, -895, -1094, -1293, -1492, 109, 328, 547, 766,
985, 1204, 1423, 1642, -109, -328, -547, -766, -985,
-1204, -1423, -1642, 120, 361, 601, 842, 1083, 1324,
1564, 1805, -120, -361, -601, -842, -1083, -1324, -1564,
-1805, 132, 397, 662, 927, 1192, 1457, 1722, 1987, -132,
-397, -662, -927, -1192, -1457, -1722, -1987, 145, 437,
728, 1020, 1311, 1603, 1894, 2186, -145, -437, -728,
-1020, -1311, -1603, -1894, -2186, 160, 480, 801, 1121,
1442, 1762, 2083, 2403, -160, -480, -801, -1121, -1442,
-1762, -2083, -2403, 176, 529, 881, 1234, 1587, 1940,
2292, 2645, -176, -529, -881, -1234, -1587, -1940, -2292,
-2645, 194, 582, 970, 1358, 1746, 2134, 2522, 2910,
-194, -582, -970, -1358, -1746, -2134, -2522, -2910,
213, 640, 1066, 1493, 1920, 2347, 2773, 3200, -213,
-640, -1066, -1493, -1920, -2347, -2773, -3200, 234,
704, 1173, 1643, 2112, 2582, 3051, 3521, -234, -704,
-1173, -1643, -2112, -2582, -3051, -3521, 258, 774,
1291, 1807, 2324, 2840, 3357, 3873, -258, -774, -1291,
-1807, -2324, -2840, -3357, -3873, 284, 852, 1420, 1988,
2556, 3124, 3692, 4260, -284, -852, -1420, -1988, -2556,
-3124, -3692, -4260, 312, 937, 1561, 2186, 2811, 3436,
4060, 4685, -312, -937, -1561, -2186, -2811, -3436,
-4060, -4685, 343, 1030, 1718, 2405, 3092, 3779, 4467,
5154, -343, -1030, -1718, -2405, -3092, -3779, -4467,
-5154, 378, 1134, 1890, 2646, 3402, 4158, 4914, 5670,
-378, -1134, -1890, -2646, -3402, -4158, -4914, -5670,
415, 1247, 2079, 2911, 3742, 4574, 5406, 6238, -415,
-1247, -2079, -2911, -3742, -4574, -5406, -6238, 457,
1372, 2287, 3202, 4117, 5032, 5947, 6862, -457, -1372,
-2287, -3202, -4117, -5032, -5947, -6862, 503, 1509,
2516, 3522, 4529, 5535, 6542, 7548, -503, -1509, -2516,
-3522, -4529, -5535, -6542, -7548, 553, 1660, 2767,
3874, 4981, 6088, 7195, 8302, -553, -1660, -2767, -3874,
-4981, -6088, -7195, -8302, 608, 1826, 3044, 4262, 5479,
6697, 7915, 9133, -608, -1826, -3044, -4262, -5479,
-6697, -7915, -9133, 669, 2009, 3348, 4688, 6027, 7367,
8706, 10046, -669, -2009, -3348, -4688, -6027, -7367,
-8706, -10046, 736, 2210, 3683, 5157, 6630, 8104, 9577,
11051, -736, -2210, -3683, -5157, -6630, -8104, -9577,
-11051, 810, 2431, 4052, 5673, 7294, 8915, 10536, 12157,
-810, -2431, -4052, -5673, -7294, -8915, -10536, -12157,
891, 2674, 4457, 6240, 8023, 9806, 11589, 13372, -891,
-2674, -4457, -6240, -8023, -9806, -11589, -13372, 980,
2941, 4903, 6864, 8825, 10786, 12748, 14709, -980, -2941,
-4903, -6864, -8825, -10786, -12748, -14709, 1078, 3236,
5393, 7551, 9708, 11866, 14023, 16181, -1078, -3236,
-5393, -7551, -9708, -11866, -14023, -16181, 1186, 3559,
5933, 8306, 10679, 13052, 15426, 17799, -1186, -3559,
-5933, -8306, -10679, -13052, -15426, -17799, 1305,
3915, 6526, 9136, 11747, 14357, 16968, 19578, -1305,
-3915, -6526, -9136, -11747, -14357, -16968, -19578,
1435, 4307, 7179, 10051, 12922, 15794, 18666, 21538,
-1435, -4307, -7179, -10051, -12922, -15794, -18666,
-21538, 1579, 4738, 7896, 11055, 14214, 17373, 20531,
23690, -1579, -4738, -7896, -11055, -14214, -17373,
-20531, -23690, 1737, 5212, 8686, 12161, 15636, 19111,
22585, 26060, -1737, -5212, -8686, -12161, -15636, -19111,
-22585, -26060, 1911, 5733, 9555, 13377, 17200, 21022,
24844, 28666, -1911, -5733, -9555, -13377, -17200, -21022,
-24844, -28666, 2102, 6306, 10511, 14715, 18920, 23124,
27329, 31533, -2102, -6306, -10511, -14715, -18920,
-23124, -27329, -31533, 2312, 6937, 11562, 16187, 20812,
25437, 30062, 32767, -2312, -6937, -11562, -16187, -20812,
-25437, -30062, -32767, 2543, 7631, 12718, 17806, 22893,
27981, 32767, 32767, -2543, -7631, -12718, -17806, -22893,
-27981, -32767, -32767, 2798, 8394, 13990, 19586, 25183,
30779, 32767, 32767, -2798, -8394, -13990, -19586, -25183,
-30779, -32767, -32767, 3077, 9233, 15389, 21545, 27700,
32767, 32767, 32767, -3077, -9233, -15389, -21545, -27700,
-32767, -32767, -32767, 3385, 10157, 16928, 23700, 30471,
32767, 32767, 32767, -3385, -10157, -16928, -23700,
-30471, -32767, -32767, -32767, 3724, 11172, 18621,
26069, 32767, 32767, 32767, 32767, -3724, -11172, -18621,
-26069, -32767, -32767, -32767, -32767, 4095, 12287,
20479, 28671, 32767, 32767, 32767, 32767, -4095, -12287,
-20479, -28671, -32767, -32767, -32767, -32767
};
void static decodeSamples(int32 volumeLevel, int32 stepSizeIdx, int32 initSampleCount, int32 predictorValue, int16 *outputBuffer, byte *compressedBuffer) {
if (volumeLevel == 0)
return;
// The game originally used 17 practically identical functions, one
// for each volume level. This is a neat way to only write a single
// function, and still replicate perfectly what should happen...
static const struct VolumeParams {
int multiplier;
int shift;
} volumeParams[17] = {
{0, 0}, // Volume 0
{1, 4}, // Volume 1
{1, 3}, // Volume 2
{3, 4}, // Volume 3
{1, 2}, // Volume 4
{5, 4}, // Volume 5
{3, 3}, // Volume 6
{7, 4}, // Volume 7
{1, 1}, // Volume 8
{9, 4}, // Volume 9
{5, 3}, // Volume 10
{11, 4}, // Volume 11
{3, 2}, // Volume 12
{13, 4}, // Volume 13
{7, 3}, // Volume 14
{15, 4}, // Volume 15
{1, 0} // Volume 16
};
const VolumeParams &params = volumeParams[volumeLevel];
uint32 stepSize = stepSizeIdx;
int32 initialPredictor = predictorValue;
int32 idx;
for (int32 count = initSampleCount; count < 735; count++) {
// Process and write the first sample...
idx = (compressedBuffer[count] >> 4);
int32 sample1 = adpcmDeltaTable[idx + (stepSize >> 2)] + initialPredictor;
int32 newStepSize = adpcmStepSizeTable[idx + (stepSize >> 2)];
if (sample1 > 0x7FFF) {
sample1 = 0x7FFF;
}
if (sample1 < -0x7FFF) {
sample1 = -0x7FFF;
}
outputBuffer[2 * count] = (int16)((params.multiplier * sample1) >> params.shift);
// Process and write the second sample...
idx = (compressedBuffer[count] & 0xF);
if (idx + (stepSize >> 2) > 1424) {
warning("invalid stepSize %d >> 2 == %d", stepSize, (stepSize >> 2));
stepSize = 0;
}
int32 sample2 = adpcmDeltaTable[idx + (newStepSize >> 2)] + sample1;
stepSize = adpcmStepSizeTable[idx + (newStepSize >> 2)];
if (sample2 > 0x7FFF) {
sample2 = 0x7FFF;
}
if (sample2 < -0x7FFF) {
sample2 = -0x7FFF;
}
outputBuffer[2 * count + 1] = (int16)((params.multiplier * sample2) >> params.shift);
initialPredictor = sample2;
}
}
void SoundManager::mix(Slot *slot, int16 *outBuf) {
int16 *currentDataPtr = (int16 *)slot->_currentDataPtr;
int32 predictorValue = READ_LE_INT16(&currentDataPtr[0]);
uint32 stepSizeIndex = READ_LE_INT16(&currentDataPtr[1]) << 6;
if (stepSizeIndex > 5632) {
slot->_statusFlags |= kSoundFlagDecodeError;
} else {
// decodeSamples() is an ADPCM decoder,
// receiving the following params:
// 1. Volume
// 2. Step size index
// 3. Initial sample count (0)
// 4. Predictor value
// 5. Output buffer
// 6. Compressed ADPCM data pointer
Slot *slotTmp = slot;
decodeSamples(
(slot->_statusFlags & kSoundVolumeMask),
stepSizeIndex,
0,
predictorValue,
outBuf,
(byte *)(currentDataPtr + 2));
slotTmp->_currentDataPtr += 739;
}
}
void SoundManager::mixEngine() {
int numChannels;
int volume;
int fadeDelayCounter;
int statusFlags;
Slot *cachedSlot;
Slot *nextCachedSlot;
Slot *soundChannels[6];
if (_soundEngineToggle) {
_soundDriverFlags |= kSoundDriverStarted;
} else {
_soundEngineToggle = true;
if ((_soundDriverFlags & kSoundDriverNISHasRequestedDelay) == 0)
++_soundDriverTicks;
int16 *mixBuffer = _soundMixBuffer;
if ((_soundDriverFlags & kSoundFlagUnmuteRequested) != 0)
_soundDriverFlags &= ~kSoundFlagUnmuteRequested;
if ((_soundDriverFlags & kSoundDriverClearBufferRequested) != 0) {
if ((_soundDriverFlags & kSoundDriverClearBufferProcessed) == 0)
_soundDriverFlags |= kSoundDriverClearBufferProcessed;
memset(_soundMixBuffer, 0, 1470 * sizeof(int16));
} else {
cachedSlot = _soundCache;
if (_soundCache) {
do {
nextCachedSlot = cachedSlot->_next;
if ((cachedSlot->_statusFlags & kSoundFlagPlayRequested) != 0) {
cachedSlot->_statusFlags |= kSoundFlagPlaying;
if ((cachedSlot->_statusFlags & kSoundFlagHasUnreadData) == 0 ||
(cachedSlot->_statusFlags & kSoundFlagMute) != 0 ||
cachedSlot->_blockCount <= 1 ||
(739 * (cachedSlot->_time + 1)) <= cachedSlot->_loadedBytes) {
cachedSlot->_statusFlags &= ~kSoundFlagDecodeStall;
} else {
cachedSlot->_statusFlags |= kSoundFlagDecodeStall;
}
}
if ((cachedSlot->_statusFlags & kSoundFlagPauseRequested) != 0)
cachedSlot->_statusFlags |= kSoundFlagPaused;
if ((cachedSlot->_statusFlags & kSoundFlagMuteRequested) != 0)
cachedSlot->_statusFlags |= kSoundFlagMuteProcessed;
cachedSlot = nextCachedSlot;
} while (nextCachedSlot);
}
cachedSlot = _soundCache;
numChannels = 0;
if (_soundCache) {
do {
nextCachedSlot = cachedSlot->_next;
if ((cachedSlot->_statusFlags & kSoundFlagPlaying) != 0 &&
(cachedSlot->_statusFlags & (kSoundFlagDecodeError | kSoundFlagDecodeStall | kSoundFlagPaused | kSoundFlagClosed | kSoundFlagCloseRequested | kSoundFlagMuteProcessed)) == 0 &&
cachedSlot->_soundBuffer &&
(cachedSlot->_statusFlags & kSoundVolumeMask) != 0 && cachedSlot->_blockCount) {
if (cachedSlot->_currentDataPtr) {
soundChannels[numChannels] = cachedSlot;
numChannels++;
} else {
cachedSlot->_statusFlags |= (kSoundFlagDecodeError | kSoundFlagCloseRequested);
}
}
cachedSlot = nextCachedSlot;
} while (nextCachedSlot);
}
_copyingDataToSoundDriver = true;
if (numChannels == 0 || (_soundDriverFlags & kSoundDriverClearBufferProcessed) != 0) {
memset(mixBuffer, 0, 1470 * sizeof(int16));
} else {
for (int j = 0; j < numChannels; j++) {
if (numChannels == 1) {
mix(soundChannels[j], mixBuffer);
} else {
mix(soundChannels[j], _soundChannelsMixBuffers[j]);
}
if ((soundChannels[j]->_statusFlags & kSoundFlagDecodeError) != 0) {
if (numChannels <= 1) {
memset(mixBuffer, 0, 1470 * sizeof(int16));
} else {
memset(_soundChannelsMixBuffers[j], 0, 1470 * sizeof(int16));
}
}
byte *currentDataPtr = soundChannels[j]->_currentDataPtr;
if (soundChannels[j]->_dataEnd <= currentDataPtr) {
if ((soundChannels[j]->_statusFlags & kSoundFlagLooped) != 0) {
soundChannels[j]->_currentDataPtr = soundChannels[j]->_dataStart + 6;
} else if ((soundChannels[j]->_statusFlags & kSoundFlagCyclicBuffer) != 0) {
soundChannels[j]->_currentDataPtr = &currentDataPtr[-soundChannels[j]->_size];
} else {
soundChannels[j]->_statusFlags |= kSoundFlagCloseRequested;
}
}
}
}
_copyingDataToSoundDriver = false;
if (numChannels > 1)
mixChannels(numChannels, mixBuffer);
cachedSlot = _soundCache;
if (_soundCache) {
do {
nextCachedSlot = cachedSlot->_next;
statusFlags = cachedSlot->_statusFlags;
if ((cachedSlot->_statusFlags & kSoundFlagPlaying) != 0 && (cachedSlot->_statusFlags & (kSoundFlagDecodeStall | kSoundFlagPaused)) == 0) {
cachedSlot->_time++;
cachedSlot->_blockCount--;
if ((statusFlags & kSoundFlagCloseOnDataEnd) != 0 && cachedSlot->_blockCount <= 0)
statusFlags |= kSoundFlagCloseRequested;
if ((statusFlags & kSoundFlagVolumeChanging) != 0) {
volume = statusFlags & kSoundVolumeMask;
if (cachedSlot->_fadeTargetVolume == volume) {
statusFlags &= ~kSoundFlagVolumeChanging;
} else {
fadeDelayCounter = cachedSlot->_fadeDelayCounter;
cachedSlot->_fadeDelayCounter = fadeDelayCounter + 1;
if (fadeDelayCounter > 3) {
if (cachedSlot->_fadeTargetVolume <= volume) {
volume--;
} else {
volume++;
}
cachedSlot->_fadeDelayCounter = 0;
}
}
if (volume <= 0)
statusFlags |= kSoundFlagCloseRequested;
statusFlags = (statusFlags & ~kSoundVolumeMask) | (volume & kSoundVolumeMask);
}
}
if ((statusFlags & kSoundFlagCloseRequested) != 0 && (statusFlags & kSoundFlagHasLinkAfter) != 0) {
cachedSlot->_chainedSound->play();
statusFlags &= ~kSoundFlagHasLinkAfter;
}
cachedSlot->_statusFlags = statusFlags;
cachedSlot = nextCachedSlot;
} while (nextCachedSlot);
}
}
cachedSlot = _soundCache;
if (_soundCache) {
do {
cachedSlot->_statusFlags &= ~kSoundFlagUnmuteRequested;
nextCachedSlot = cachedSlot->_next;
if ((cachedSlot->_statusFlags & (kSoundFlagDecodeError | kSoundFlagCloseRequested)) != 0) {
cachedSlot->_statusFlags |= kSoundFlagClosed;
}
cachedSlot = nextCachedSlot;
} while (nextCachedSlot);
}
_soundEngineToggle = false;
}
}
void SoundManager::mixChannels(int numChannels, int16 *buffer) {
int sampleVal;
// This is actually how they mix channels in the original :-)
// It's probably done this way because as a possible optimization;
// we shouldn't need this, but for now I'll replicate exactly what was done.
switch (numChannels) {
case 2:
for (int i = 0; i < 1470; i++) {
sampleVal = _soundChannelsMixBuffers[0][i] +
_soundChannelsMixBuffers[1][i];
if (sampleVal > 0x7FFF)
sampleVal = 0x7FFF;
if (sampleVal < -0x7FFF)
sampleVal = -0x7FFF;
buffer[i] = (int16)sampleVal;
}
break;
case 3:
for (int i = 0; i < 1470; i++) {
sampleVal = _soundChannelsMixBuffers[0][i] +
_soundChannelsMixBuffers[1][i] +
_soundChannelsMixBuffers[2][i];
if (sampleVal > 0x7FFF)
sampleVal = 0x7FFF;
if (sampleVal < -0x7FFF)
sampleVal = -0x7FFF;
buffer[i] = (int16)sampleVal;
}
break;
case 4:
for (int i = 0; i < 1470; i++) {
sampleVal = _soundChannelsMixBuffers[0][i] +
_soundChannelsMixBuffers[1][i] +
_soundChannelsMixBuffers[2][i] +
_soundChannelsMixBuffers[3][i];
if (sampleVal > 0x7FFF)
sampleVal = 0x7FFF;
if (sampleVal < -0x7FFF)
sampleVal = -0x7FFF;
buffer[i] = (int16)sampleVal;
}
break;
case 5:
for (int i = 0; i < 1470; i++) {
sampleVal = _soundChannelsMixBuffers[0][i] +
_soundChannelsMixBuffers[1][i] +
_soundChannelsMixBuffers[2][i] +
_soundChannelsMixBuffers[3][i] +
_soundChannelsMixBuffers[4][i];
if (sampleVal > 0x7FFF)
sampleVal = 0x7FFF;
if (sampleVal < -0x7FFF)
sampleVal = -0x7FFF;
buffer[i] = (int16)sampleVal;
}
break;
case 6:
for (int i = 0; i < 1470; i++) {
sampleVal = _soundChannelsMixBuffers[0][i] +
_soundChannelsMixBuffers[1][i] +
_soundChannelsMixBuffers[2][i] +
_soundChannelsMixBuffers[3][i] +
_soundChannelsMixBuffers[4][i] +
_soundChannelsMixBuffers[5][i];
if (sampleVal > 0x7FFF)
sampleVal = 0x7FFF;
if (sampleVal < -0x7FFF)
sampleVal = -0x7FFF;
buffer[i] = (int16)sampleVal;
}
break;
default:
return;
}
}
} // End of namespace LastExpress

View File

@@ -0,0 +1,912 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "lastexpress/sound/slot.h"
#include "lastexpress/sound/sound.h"
#include "lastexpress/sound/subtitle.h"
#include "lastexpress/data/archive.h"
#include "lastexpress/helpers.h"
#include "lastexpress/lastexpress.h"
namespace LastExpress {
Slot::Slot(SoundManager *soundMan, const char *filename, int typeFlags, int priority) {
_soundMan = soundMan;
_engine = _soundMan->_engine;
_engine->_soundMutex->lock();
memset(_name1, 0, sizeof(_name1));
memset(_name2, 0, sizeof(_name2));
_priority = priority;
setTag(typeFlags);
setType(typeFlags);
_soundMan->addSlot(this);
_engine->_soundMutex->unlock();
// From this point onwards there could be deadlocks between the main
// thread and the sound thread because of busy waits in getBuffer(),
// so we leave the mutex unlocked and let the insides of the function
// do their job...
getBuffer();
// This function will reacquire the mutex...
load(filename);
}
Slot::Slot(SoundManager *soundMan, int typeFlags, int priority) {
_soundMan = soundMan;
_engine = _soundMan->_engine;
_engine->_soundMutex->lock();
memset(_name1, 0, sizeof(_name1));
memset(_name2, 0, sizeof(_name2));
_priority = priority;
Common::strcpy_s(_name2, "NISSND?");
assignDirectVolume(kVolumeFull);
setTag(typeFlags);
setType(typeFlags);
_soundMan->addSlot(this);
_engine->_soundMutex->unlock();
// From this point onwards there could be deadlocks between the main
// thread and the sound thread because of busy waits in getBuffer(),
// so we leave the mutex unlocked and let the insides of the function
// do their job...
getBuffer();
}
Slot::Slot(SoundManager *soundMan, SaveSlot *soundEntry) {
_soundMan = soundMan;
_engine = _soundMan->_engine;
_engine->_soundMutex->lock();
memset(_name1, 0, sizeof(_name1));
memset(_name2, 0, sizeof(_name2));
setBlockCount(soundEntry->blockCount);
addStatusFlag(soundEntry->status);
setTag(_statusFlags);
strncpy(_name1, soundEntry->name1, 15);
strncpy(_name2, soundEntry->name2, 15);
Common::String subName = _name2;
subName.replace('.', '\0');
setSub(subName.c_str());
_time = soundEntry->time;
_fadeDelayCounter = soundEntry->fadeDelayCounter;
_unusedVar = soundEntry->unusedVar;
_character = soundEntry->character;
_delayedStartTime = _soundMan->getSoundDriver30HzCounter() + soundEntry->delayTicks;
_priority = soundEntry->priority;
_engine->_soundMutex->unlock();
// From this point onwards there could be deadlocks between the main
// thread and the sound thread because of busy waits in devirtualize(),
// so we must activate the mutex selectively when fetching or updating
// the class members...
if ((getStatusFlags() & kSoundFlagPlaying) != 0 &&
(getStatusFlags() & kSoundFlagMuteProcessed) == 0 &&
(getStatusFlags() & kSoundVolumeMask) != 0) {
removeStatusFlag(kSoundFlagPlaying);
addStatusFlag(kSoundFlagMute);
devirtualize();
}
_soundMan->addSlot(this);
}
Slot::~Slot() {
addStatusFlag(kSoundFlagCloseRequested);
while (true) {
bool mustWait = (getStatusFlags() & kSoundFlagClosed) == 0 &&
(_soundMan->getSoundDriverFlags() & kSoundDriverClearBufferProcessed) == 0 &&
(_soundMan->getSoundDriverFlags() & kSoundDriverInitState) != 0;
if (!mustWait)
break;
_engine->handleEvents(); // For good measure...
}
_soundMan->removeSlot(this);
_engine->_soundMutex->lock();
if (_archive)
_engine->getArchiveManager()->closeHPF(_archive);
_engine->_soundMutex->unlock();
if (getSoundBuffer())
releaseBuffer();
if (getSubtitle()) {
_engine->_soundMutex->lock();
delete _subtitle;
_subtitle = nullptr;
_engine->_soundMutex->unlock();
}
if (getAssociatedCharacter()) {
if (getAssociatedCharacter() == kCharacterSteam) {
_soundMan->ambientAI(kAmbientSoundSteam);
} else if (getAssociatedCharacter() != kCharacterClerk) {
_engine->getLogicManager()->send(kCharacterCath, getAssociatedCharacter(), 2, 0);
}
}
}
void Slot::play() {
if ((_soundMan->getSoundDriverFlags() & kSoundDriverNISHasRequestedFade) != 0) {
if (getTag() != kSoundTagNIS && getTag() != kSoundTagLink && getTag() != kSoundTagConcert) {
int volume = getStatusFlags() & kSoundVolumeMask;
removeStatusFlag(kSoundVolumeMask);
assignDirectVolume(volume);
addStatusFlag((volume >> 1) + 1);
}
}
addStatusFlag(kSoundFlagPlayRequested);
}
void Slot::setFade(int volume) {
Common::StackLock lock(*_engine->_soundMutex);
if ((_statusFlags & kSoundFlagFading) == 0) {
int32 targetVolume = volume;
_statusFlags |= kSoundFlagVolumeChanging;
if (volume) {
if ((_soundMan->_soundDriverFlags & kSoundDriverNISHasRequestedFade) != 0) {
_volume = volume;
targetVolume = (volume >> 1) + 1;
}
_fadeTargetVolume = targetVolume;
} else {
_fadeTargetVolume = 0;
_statusFlags |= kSoundFlagFading;
}
}
}
void Slot::setVol(int volume) {
if (volume) {
if ((_soundMan->getSoundDriverFlags() & kSoundDriverNISHasRequestedFade) == 0 || getTag() == kSoundTagNIS || getTag() == kSoundTagLink) {
setStatusFlags(volume + (getStatusFlags() & ~kSoundVolumeMask));
} else {
setFade(volume);
}
} else {
assignDirectVolume(0);
addStatusFlag(kSoundFlagMuteRequested);
removeStatusFlag(kSoundVolumeMask);
removeStatusFlag(kSoundFlagVolumeChanging);
}
}
void Slot::setTag(int typeFlags) {
Slot *cacheSlot;
switch (typeFlags & kSoundTypeMask) {
case kSoundTypeNormal:
assignDirectTag(_soundMan->_curSoundSlotTag++);
break;
case kSoundTypeAmbient:
cacheSlot = _soundMan->_soundCache;
if (cacheSlot) {
do {
if (cacheSlot->getTag() == kSoundTagOldAmbient)
break;
cacheSlot = cacheSlot->getNext();
} while (cacheSlot);
if (cacheSlot)
cacheSlot->setFade(0);
}
cacheSlot = _soundMan->_soundCache;
if (cacheSlot) {
do {
if (cacheSlot->getTag() == kSoundTagAmbient)
break;
cacheSlot = cacheSlot->getNext();
} while (cacheSlot);
if (cacheSlot) {
cacheSlot->assignDirectTag(kSoundTagOldAmbient);
cacheSlot->setFade(0);
}
}
assignDirectTag(kSoundTagAmbient);
break;
case kSoundTypeMenu:
cacheSlot = _soundMan->_soundCache;
if (cacheSlot) {
do {
if (cacheSlot->getTag() == kSoundTagMenu)
break;
cacheSlot = cacheSlot->getNext();
} while (cacheSlot);
if (cacheSlot)
cacheSlot->assignDirectTag(kSoundTagOldMenu);
}
assignDirectTag(kSoundTagMenu);
break;
case kSoundTypeLink:
cacheSlot = _soundMan->_soundCache;
if (cacheSlot) {
do {
if (cacheSlot->getTag() == kSoundTagLink)
break;
cacheSlot = cacheSlot->getNext();
} while (cacheSlot);
if (cacheSlot)
cacheSlot->assignDirectTag(kSoundTagOldLink);
}
assignDirectTag(kSoundTagLink);
break;
case kSoundTypeIntro:
cacheSlot = _soundMan->_soundCache;
if (cacheSlot) {
do {
if (cacheSlot->getTag() == kSoundTagIntro)
break;
cacheSlot = cacheSlot->getNext();
} while (cacheSlot);
if (cacheSlot)
cacheSlot->assignDirectTag(kSoundTagOldMenu);
}
assignDirectTag(kSoundTagIntro);
break;
case kSoundTypeWalla:
cacheSlot = _soundMan->_soundCache;
if (cacheSlot) {
do {
if (cacheSlot->getTag() == kSoundTagWalla)
break;
cacheSlot = cacheSlot->getNext();
} while (cacheSlot);
if (cacheSlot) {
cacheSlot->assignDirectTag(kSoundTagOldWalla);
cacheSlot->setFade(0);
}
}
assignDirectTag(kSoundTagWalla);
break;
case kSoundTypeNIS:
cacheSlot = _soundMan->_soundCache;
if (cacheSlot) {
do {
if (cacheSlot->getTag() == kSoundTagNIS)
break;
cacheSlot = cacheSlot->getNext();
} while (cacheSlot);
if (cacheSlot)
cacheSlot->assignDirectTag(kSoundTagOldNIS);
}
assignDirectTag(kSoundTagNIS);
break;
default:
break;
}
}
void Slot::setType(int typeFlags) {
int effFlags = typeFlags;
if ((typeFlags & kSoundVolumeMask) == 0)
effFlags |= kSoundFlagMuteRequested;
if ((effFlags & kSoundFlagLooped) == 0)
effFlags |= kSoundFlagCloseOnDataEnd;
addStatusFlag(effFlags);
}
void Slot::setSub(const char *filename) {
Common::StackLock lock(*_engine->_soundMutex);
_subtitle = new Subtitle(_engine, filename, this);
if ((_subtitle->_status & kSubFlagStatusKilled) != 0) {
delete _subtitle;
_subtitle = nullptr;
} else {
_statusFlags |= kSoundFlagHasSubtitles;
}
}
bool Slot::getBuffer() {
int effPrio = 1000;
int count = 0;
Slot *cacheSlots;
Slot *chosenSlot;
if (getSoundBuffer()) {
return true;
}
if (_soundMan->_numActiveChannels >= 6) {
cacheSlots = _soundMan->_soundCache;
for (chosenSlot = nullptr; cacheSlots; cacheSlots = cacheSlots->getNext()) {
if ((cacheSlots->getStatusFlags() & kSoundFlagMute) == 0) {
if (cacheSlots->getPriority() + (int)(getStatusFlags() & kSoundVolumeMask) < effPrio) {
chosenSlot = cacheSlots;
effPrio = cacheSlots->getPriority() + (getStatusFlags() & kSoundVolumeMask);
}
}
}
if (getPriority() <= effPrio) {
return false;
} else {
if (chosenSlot)
chosenSlot->virtualize();
if (chosenSlot) {
while ((chosenSlot->getStatusFlags() & kSoundFlagMuteProcessed) == 0) {
_engine->handleEvents();
}
}
if (chosenSlot && chosenSlot->getSoundBuffer())
chosenSlot->releaseBuffer();
for (count = 0; _soundMan->_soundSlotChannels[count]; count++) {
if (count >= ARRAYSIZE(_soundMan->_soundSlotChannels) - 1) {
return false;
}
}
_soundMan->_soundSlotChannels[count] = this;
_soundMan->_numActiveChannels++;
setSoundBuffer(_engine->_soundMemoryPool + 0x16800 * count);
return true;
}
} else {
for (count = 0; _soundMan->_soundSlotChannels[count]; count++) {
if (count >= ARRAYSIZE(_soundMan->_soundSlotChannels) - 1) {
return false;
}
}
_soundMan->_soundSlotChannels[count] = this;
_soundMan->_numActiveChannels++;
setSoundBuffer(_engine->_soundMemoryPool + 0x16800 * count);
return true;
}
}
void Slot::releaseBuffer() {
Common::StackLock lock(*_engine->_soundMutex);
int count = 0;
int32 size = 0;
if (getSoundBuffer()) {
while ((size + _engine->_soundMemoryPool) != getSoundBuffer()) {
count++;
size += 0x16800;
if (size >= 0x87000)
return;
}
if (_soundMan->_soundSlotChannels[count]) {
_soundMan->_soundSlotChannels[count] = nullptr;
_soundMan->_numActiveChannels--;
setSoundBuffer(nullptr);
}
}
}
void Slot::virtualize() {
addStatusFlag(kSoundFlagMuteRequested);
}
void Slot::devirtualize() {
Slot *i;
int32 chunkSize;
int32 diff;
int32 pagesToRead;
_engine->_soundMutex->lock();
if ((_statusFlags & kSoundFlagMuteProcessed) != 0 && getBuffer()) {
if ((_statusFlags & kSoundFlagLooped) != 0) {
load(_name2);
_statusFlags &= ~kSoundFlagMute;
} else {
_archive = _engine->getArchiveManager()->openHPF(_name2);
if (_archive) {
_statusFlags |= kSoundFlagUnmuteRequested;
if ((_statusFlags & kSoundFlagPlaying) != 0) {
_statusFlags |= kSoundFlagPauseRequested;
} else {
_statusFlags |= kSoundFlagPaused;
}
if ((_statusFlags & kSoundFlagHeaderProcessed) == 0) {
_statusFlags |= kSoundFlagHeaderProcessed;
_engine->getArchiveManager()->readHPF(_archive, _soundBuffer, 1);
_engine->_soundMutex->unlock();
while ((getStatusFlags() & kSoundFlagPaused) == 0) {
_engine->handleEvents();
}
_engine->_soundMutex->lock();
_size = READ_LE_UINT32(&_soundBuffer[0]);
_blockCount = READ_LE_UINT32(&_soundBuffer[4]) - 1;
_blockCount -= _time;
}
_engine->_soundMutex->unlock();
while ((getStatusFlags() & kSoundFlagPaused) == 0) {
_engine->handleEvents();
}
_engine->_soundMutex->lock();
chunkSize = 739 * _time + 6;
_dataStart = _soundBuffer;
_dataEnd = (_dataStart + 0x16000);
_size = 0x16000;
_currentDataPtr = &_dataStart[chunkSize & 0x7FF];
_loadedBytes = chunkSize & 0xFFFF800;
diff = (_archive->size * MEM_PAGE_SIZE) - (chunkSize & 0xFFFF800);
if (diff <= 0x15800) {
pagesToRead = (diff + 2047) / MEM_PAGE_SIZE;
} else {
pagesToRead = 43;
_statusFlags |= (kSoundFlagCyclicBuffer | kSoundFlagHasUnreadData);
}
_engine->getArchiveManager()->seekHPF(_archive, chunkSize / MEM_PAGE_SIZE);
_engine->getArchiveManager()->readHPF(_archive, _soundBuffer, pagesToRead);
if (_archive->size <= _archive->currentPos) {
_engine->getArchiveManager()->closeHPF(_archive);
_statusFlags &= ~kSoundFlagHasUnreadData;
_archive = 0;
_loadedBytes = 4;
}
_loadedBytes += pagesToRead * MEM_PAGE_SIZE;
_currentBufferPtr = &_soundBuffer[pagesToRead * MEM_PAGE_SIZE];
_statusFlags &= ~(kSoundFlagMute | kSoundFlagPaused | kSoundFlagPauseRequested);
} else {
for (i = _soundMan->_soundCache; i; i = i->_next) {
if (i->_tag == kSoundTagNIS)
break;
}
if (i != this)
_statusFlags |= kSoundFlagCloseRequested;
}
}
}
_engine->_soundMutex->unlock();
}
bool Slot::load(const char *filename) {
Common::StackLock lock(*_engine->_soundMutex);
_archive = _engine->getArchiveManager()->openHPF(filename);
Common::strcpy_s(_name2, filename);
if (!_archive)
_archive = _engine->getArchiveManager()->openHPF("DEFAULT.SND");
if (_archive) {
if (_archive->size > 44 && (_statusFlags & kSoundFlagLooped) != 0) {
_engine->getArchiveManager()->closeHPF(_archive);
_archive = nullptr;
_statusFlags = kSoundFlagCloseRequested;
_loadedBytes = 5;
} else {
if (_soundBuffer) {
_dataStart = _soundBuffer;
_currentDataPtr = _soundBuffer + 6;
if (_archive->size > 44) {
_engine->getArchiveManager()->readHPF(_archive, _soundBuffer, 44);
_loadedBytes = 0x16000;
_size = 0x16000;
_statusFlags |= (kSoundFlagCloseOnDataEnd | kSoundFlagCyclicBuffer | kSoundFlagHasUnreadData);
_blockCount = (int32)READ_LE_UINT16(_soundBuffer + 4) - 1;
_dataEnd = _dataStart + 0x16000;
} else {
_engine->getArchiveManager()->readHPF(_archive, _soundBuffer, _archive->size);
_loadedBytes = _archive->size * MEM_PAGE_SIZE;
_engine->getArchiveManager()->closeHPF(_archive);
_archive = nullptr;
_loadedBytes = 6;
_blockCount = (int32)READ_LE_UINT16(_soundBuffer + 4);
_size = 739 * _blockCount;
_dataEnd = &_dataStart[_size + 6];
}
_currentBufferPtr = _dataStart;
_statusFlags |= kSoundFlagHeaderProcessed;
memset(_dataEnd, 0, 739);
return true;
} else {
_size = _archive->size * MEM_PAGE_SIZE;
if (_size > 0x16000)
_statusFlags |= (kSoundFlagCyclicBuffer | kSoundFlagHasUnreadData);
_blockCount = (_size + 1018) / 739;
_statusFlags |= kSoundFlagMute;
_engine->getArchiveManager()->closeHPF(_archive);
_archive = nullptr;
_loadedBytes = 6;
return true;
}
}
} else {
_statusFlags = kSoundFlagCloseRequested;
}
return false;
}
void Slot::stream() {
Common::StackLock lock(*_engine->_soundMutex);
int32 numPages;
int32 numBytes;
if ((_statusFlags & kSoundFlagHasUnreadData) != 0) {
if (_soundBuffer) {
if ((_statusFlags & kSoundFlagMute) == 0) {
if (_archive) {
numPages = 739 * _time - _loadedBytes + _size - 2054;
if (numPages > _size)
numPages = 0;
numBytes = numPages / MEM_PAGE_SIZE;
if (numBytes > 16) {
if (_archive->size - _archive->currentPos <= numBytes) {
_statusFlags &= ~kSoundFlagHasUnreadData;
numBytes = _archive->size - _archive->currentPos;
}
while (numBytes > 0) {
_engine->getArchiveManager()->readHPF(_archive, _currentBufferPtr, 1);
_loadedBytes += 0x800;
if (_dataStart == _currentBufferPtr)
memcpy(_dataEnd, _currentBufferPtr, 0x800);
_currentBufferPtr += 0x800;
if (_currentBufferPtr > _dataEnd)
_currentBufferPtr = _dataStart;
if (_currentBufferPtr == _dataEnd)
_currentBufferPtr = _dataStart;
numBytes--;
}
if ((_statusFlags & kSoundFlagHasUnreadData) == 0) {
_engine->getArchiveManager()->closeHPF(_archive);
_archive = nullptr;
_loadedBytes |= 1;
}
}
}
}
}
}
}
bool Slot::update() {
Common::StackLock lock(*_engine->_soundMutex);
if ((_statusFlags & kSoundFlagClosed) != 0)
return false;
if ((_statusFlags & kSoundFlagDelayedActivate) != 0) {
if (_delayedStartTime <= _soundMan->_sound30HzCounter) {
_statusFlags |= kSoundFlagPlayRequested;
_statusFlags &= ~kSoundFlagDelayedActivate;
Common::String subName = _name2;
subName.replace('.', '\0');
setSub(subName.c_str());
}
} else {
if ((_soundMan->_soundDriverFlags & kSoundDriverNISHasRequestedFade) == 0 && (_statusFlags & kSoundFlagFixedVolume) == 0) {
if (_character > 0 && _character < 128) {
setVol(_engine->getLogicManager()->getVolume(_character));
}
}
if ((_statusFlags & kSoundFlagHasUnreadData) != 0 && (_statusFlags & kSoundFlagMute) == 0 && _soundBuffer)
stream();
}
return true;
}
int32 Slot::getStatusFlags() {
Common::StackLock lock(*_engine->_soundMutex);
return _statusFlags;
}
void Slot::setStatusFlags(int32 flags) {
Common::StackLock lock(*_engine->_soundMutex);
_statusFlags = flags;
}
void Slot::addStatusFlag(int32 flag) {
Common::StackLock lock(*_engine->_soundMutex);
_statusFlags |= flag;
}
void Slot::removeStatusFlag(int32 flag) {
Common::StackLock lock(*_engine->_soundMutex);
_statusFlags &= ~flag;
}
int32 Slot::getTag() {
Common::StackLock lock(*_engine->_soundMutex);
return _tag;
}
bool Slot::hasTag(int32 tag) {
Common::StackLock lock(*_engine->_soundMutex);
return _tag == tag;
}
void Slot::assignDirectTag(int32 tag) {
Common::StackLock lock(*_engine->_soundMutex);
_tag = tag;
}
int Slot::getAssociatedCharacter() {
Common::StackLock lock(*_engine->_soundMutex);
return _character;
}
void Slot::setAssociatedCharacter(int character) {
Common::StackLock lock(*_engine->_soundMutex);
_character = character;
}
int32 Slot::getTime() {
Common::StackLock lock(*_engine->_soundMutex);
return _time;
}
Slot *Slot::getNext() {
Common::StackLock lock(*_engine->_soundMutex);
return _next;
}
void Slot::setNext(Slot *next) {
Common::StackLock lock(*_engine->_soundMutex);
_next = next;
}
int Slot::getPriority() {
Common::StackLock lock(*_engine->_soundMutex);
return _priority;
}
bool Slot::closeArchive() {
Common::StackLock lock(*_engine->_soundMutex);
bool hadArchive = false;
if (_archive) {
_engine->getArchiveManager()->closeHPF(_archive);
hadArchive = true;
}
_archive = nullptr;
return hadArchive;
}
void Slot::assignDirectVolume(int volume) {
Common::StackLock lock(*_engine->_soundMutex);
_volume = volume;
}
int Slot::getVolume() {
Common::StackLock lock(*_engine->_soundMutex);
return _volume;
}
void Slot::setChainedSound(Slot *chainedSound) {
Common::StackLock lock(*_engine->_soundMutex);
_chainedSound = chainedSound;
}
Subtitle *Slot::getSubtitle() {
Common::StackLock lock(*_engine->_soundMutex);
return _subtitle;
}
int32 Slot::getSize() {
Common::StackLock lock(*_engine->_soundMutex);
return _size;
}
void Slot::setSize(int32 size) {
Common::StackLock lock(*_engine->_soundMutex);
_size = size;
}
int32 Slot::getNumLoadedBytes() {
Common::StackLock lock(*_engine->_soundMutex);
return _loadedBytes;
}
void Slot::setNumLoadedBytes(int32 bytes) {
Common::StackLock lock(*_engine->_soundMutex);
_loadedBytes = bytes;
}
void Slot::advanceLoadedBytesBy(int32 loadedBytes) {
Common::StackLock lock(*_engine->_soundMutex);
_loadedBytes += loadedBytes;
}
byte *Slot::getDataStart() {
Common::StackLock lock(*_engine->_soundMutex);
return _dataStart;
}
void Slot::setDataStart(byte *dataStart) {
Common::StackLock lock(*_engine->_soundMutex);
_dataStart = dataStart;
}
byte *Slot::getDataEnd() {
Common::StackLock lock(*_engine->_soundMutex);
return _dataEnd;
}
void Slot::setDataEnd(byte *dataEnd) {
Common::StackLock lock(*_engine->_soundMutex);
_dataEnd = dataEnd;
}
void Slot::setDataEndByte(int32 pos, byte value) {
Common::StackLock lock(*_engine->_soundMutex);
_dataEnd[pos] = value;
}
void Slot::advanceDataEndBy(int32 size) {
Common::StackLock lock(*_engine->_soundMutex);
_dataEnd += size;
}
byte *Slot::getSoundBuffer() {
Common::StackLock lock(*_engine->_soundMutex);
return _soundBuffer;
}
void Slot::setSoundBuffer(byte *bufferPtr) {
Common::StackLock lock(*_engine->_soundMutex);
_soundBuffer = bufferPtr;
}
byte *Slot::getCurrentBufferPtr() {
Common::StackLock lock(*_engine->_soundMutex);
return _currentBufferPtr;
}
void Slot::advanceCurrentBufferPtrBy(int32 size) {
Common::StackLock lock(*_engine->_soundMutex);
_currentBufferPtr += size;
}
void Slot::setCurrentBufferPtr(byte *bufferPtr) {
Common::StackLock lock(*_engine->_soundMutex);
_currentBufferPtr = bufferPtr;
}
byte *Slot::getCurrentDataPtr() {
Common::StackLock lock(*_engine->_soundMutex);
return _currentDataPtr;
}
void Slot::setCurrentDataPtr(byte *dataPtr) {
Common::StackLock lock(*_engine->_soundMutex);
_currentDataPtr = dataPtr;
}
int32 Slot::getBlockCount() {
Common::StackLock lock(*_engine->_soundMutex);
return _blockCount;
}
void Slot::setBlockCount(int32 blockCount) {
Common::StackLock lock(*_engine->_soundMutex);
_blockCount = blockCount;
}
void Slot::setDelayedStartTime(int32 time) {
Common::StackLock lock(*_engine->_soundMutex);
_delayedStartTime = time;
}
char *Slot::getName2() {
Common::StackLock lock(*_engine->_soundMutex);
return _name2;
}
} // End of namespace LastExpress

View File

@@ -0,0 +1,170 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LASTEXPRESS_SOUND_SLOT_H
#define LASTEXPRESS_SOUND_SLOT_H
#include "lastexpress/shared.h"
#include "lastexpress/lastexpress.h"
#include "lastexpress/sound/sound.h"
namespace LastExpress {
class LastExpressEngine;
class Subtitle;
class SoundManager;
struct HPF;
#include "common/pack-start.h"
typedef struct SaveSlot {
int32 status;
int32 tag;
int32 blockCount;
int32 time;
int32 fadeDelayCounter;
int32 unusedVar;
int32 character;
int32 delayTicks;
int32 priority;
char name1[16];
char name2[16];
SaveSlot() {
status = 0;
tag = 0;
blockCount = 0;
time = 0;
fadeDelayCounter = 0;
unusedVar = 0;
character = 0;
delayTicks = 0;
priority = 0;
memset(name1, 0, 16);
memset(name2, 0, 16);
}
} SaveSlot;
#include "common/pack-end.h"
class Slot {
friend class Subtitle;
friend class LastExpressEngine;
friend class SoundManager;
friend class LogicManager;
friend class SubtitleManager;
friend class Menu;
public:
Slot(SoundManager *soundMan, const char *filename, int typeFlags, int priority);
Slot(SoundManager *soundMan, int typeFlags, int priority);
Slot(SoundManager *soundMan, SaveSlot *soundEntry);
~Slot();
void play();
void setFade(int volume);
void setVol(int volume);
void setTag(int typeFlags);
void setType(int typeFlags);
void setSub(const char *filename);
bool getBuffer();
void releaseBuffer();
void virtualize();
void devirtualize();
bool load(const char *filename);
void stream();
bool update();
// The following getters/setters were not in the original
// but are needed for thread safety
int32 getStatusFlags();
void setStatusFlags(int32 flags);
void addStatusFlag(int32 flag);
void removeStatusFlag(int32 flag);
int32 getTag();
bool hasTag(int32 tag);
void assignDirectTag(int32 tag);
int getAssociatedCharacter();
void setAssociatedCharacter(int character);
int32 getTime();
Slot *getNext();
void setNext(Slot *next);
int getPriority();
bool closeArchive();
void assignDirectVolume(int volume);
int getVolume();
void setChainedSound(Slot *chainedSound);
Subtitle *getSubtitle();
int32 getSize();
void setSize(int32 size);
int32 getNumLoadedBytes();
void setNumLoadedBytes(int32 bytes);
void advanceLoadedBytesBy(int32 loadedBytes);
byte *getDataStart();
void setDataStart(byte *dataStart);
byte *getDataEnd();
void setDataEnd(byte *dataEnd);
void setDataEndByte(int32 pos, byte value);
void advanceDataEndBy(int32 size);
byte *getSoundBuffer();
void setSoundBuffer(byte *bufferPtr);
byte *getCurrentBufferPtr();
void advanceCurrentBufferPtrBy(int32 size);
void setCurrentBufferPtr(byte *bufferPtr);
byte *getCurrentDataPtr();
void setCurrentDataPtr(byte *dataPtr);
int32 getBlockCount();
void setBlockCount(int32 blockCount);
void setDelayedStartTime(int32 time);
char *getName2();
private:
LastExpressEngine *_engine = nullptr;
SoundManager *_soundMan = nullptr;
int32 _statusFlags = 0;
int32 _tag = 0;
byte *_dataStart = nullptr;
byte *_dataEnd = 0;
byte *_currentDataPtr = nullptr;
byte *_soundBuffer = nullptr;
byte *_currentBufferPtr = nullptr;
int32 _blockCount = 0;
int32 _time = 0;
int32 _size = 0;
int32 _loadedBytes = 0;
HPF *_archive = nullptr;
Slot *_chainedSound = nullptr;
int32 _fadeDelayCounter = 0;
int32 _unusedVar = 0;
int32 _fadeTargetVolume = 0;
int _volume = 0;
int _character = 0;
int32 _delayedStartTime = 0;
int _priority = 0;
char _name1[16];
char _name2[16];
Slot *_next = nullptr;
Subtitle *_subtitle = nullptr;
};
} // End of namespace LastExpress
#endif // LASTEXPRESS_SOUND_SLOT_H

View File

@@ -0,0 +1,714 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "lastexpress/sound/sound.h"
#include "lastexpress/lastexpress.h"
namespace LastExpress {
SoundManager::SoundManager(LastExpressEngine *engine) {
_engine = engine;
_mixer = _engine->_mixer;
for (int i = 0; i < ARRAYSIZE(_soundSlotChannels); i++) {
_soundSlotChannels[i] = nullptr;
}
memset(_soundChannelsMixBuffers, 0, sizeof(_soundChannelsMixBuffers));
soundDriverInit();
}
SoundManager::~SoundManager() {
_engine = nullptr;
}
int SoundManager::playSoundFile(const char *sndName, int typeFlags, int character, int delay) {
Slot *slot = new Slot(this, sndName, typeFlags, 30);
slot->setAssociatedCharacter(character);
if (delay) {
slot->setDelayedStartTime(getSoundDriver30HzCounter() + 2 * delay);
slot->addStatusFlag(kSoundFlagDelayedActivate);
} else {
Common::String subName = sndName;
subName.replace('.', '\0');
slot->setSub(subName.c_str());
slot->play();
}
return slot->getTag();
}
void SoundManager::startAmbient() {
_soundSlotAmbientFlag |= kAmbientSoundEnabled;
}
void SoundManager::startSteam(int cityIndex) {
Slot *cachedSlot;
bool playSoundNeeded = true;
_soundSlotAmbientFlag |= kAmbientSoundSteam;
if (_soundCache) {
cachedSlot = _soundCache;
do {
if (cachedSlot->hasTag(kSoundTagAmbient))
break;
cachedSlot = cachedSlot->getNext();
} while (cachedSlot);
if (cachedSlot)
playSoundNeeded = false;
}
if (playSoundNeeded)
playSoundFile("STEAM.SND", kSoundTypeAmbient | kSoundFlagLooped | kVolume7, kCharacterSteam, 0);
cachedSlot = _soundCache;
if (_soundCache) {
do {
if (cachedSlot->hasTag(kSoundTagAmbient))
break;
cachedSlot = cachedSlot->getNext();
} while (cachedSlot);
if (cachedSlot) {
cachedSlot = _soundCache;
if (_soundCache) {
do {
if (cachedSlot->hasTag(kSoundTagAmbient))
break;
cachedSlot = cachedSlot->getNext();
} while (cachedSlot);
if (cachedSlot) {
cachedSlot->setSub(_cities[cityIndex]);
}
}
}
}
}
void SoundManager::endAmbient() {
_soundSlotAmbientFlag = 0;
Slot *cachedSlot = _soundCache;
if (_soundCache) {
do {
if (cachedSlot->hasTag(kSoundTagAmbient))
break;
cachedSlot = cachedSlot->getNext();
} while (cachedSlot);
if (cachedSlot)
cachedSlot->setFade(0);
}
cachedSlot = _soundCache;
if (_soundCache) {
do {
if (cachedSlot->hasTag(kSoundTagOldAmbient))
break;
cachedSlot = cachedSlot->getNext();
} while (cachedSlot);
if (cachedSlot)
cachedSlot->setFade(0);
}
}
void SoundManager::killAmbient() {
_soundSlotAmbientFlag = 0;
Slot *cachedSlot = _soundCache;
if (_soundCache) {
do {
if (cachedSlot->hasTag(kSoundTagAmbient))
break;
cachedSlot = cachedSlot->getNext();
} while (cachedSlot);
if (cachedSlot) {
cachedSlot->addStatusFlag(kSoundFlagCloseRequested);
cachedSlot->setAssociatedCharacter(0);
cachedSlot->closeArchive();
}
}
cachedSlot = _soundCache;
if (_soundCache) {
do {
if (cachedSlot->hasTag(kSoundTagOldAmbient))
break;
cachedSlot = cachedSlot->getNext();
} while (cachedSlot);
if (cachedSlot) {
cachedSlot->addStatusFlag(kSoundFlagCloseRequested);
cachedSlot->setAssociatedCharacter(0);
cachedSlot->closeArchive();
}
}
}
void SoundManager::raiseAmbient(int level, int delay) {
if (level > 7) {
_soundAmbientFadeLevel = level;
_soundAmbientFadeTime = getSoundDriver30HzCounter() + 2 * delay;
}
}
void SoundManager::levelAmbient(int delay) {
_soundAmbientFadeLevel = 7;
_soundAmbientFadeTime = getSoundDriver30HzCounter() + 2 * delay;
}
Slot *SoundManager::findSlotWho(int32 character) {
Slot *result = _soundCache;
if (!_soundCache)
return nullptr;
while (result->getAssociatedCharacter() != character) {
result = result->getNext();
if (!result)
return nullptr;
}
return result;
}
Slot *SoundManager::findSlotName(char *name) {
Common::StackLock lock(*_engine->_soundMutex);
Slot *result = _soundCache;
if (!_soundCache)
return nullptr;
while (scumm_stricmp(name, result->getName2())) {
result = result->getNext();
if (!result)
return nullptr;
}
return result;
}
void SoundManager::ambientAI(int id) {
Slot *oldAmbientSlot1;
Slot *oldAmbientSlot2;
uint fileNameLen = 6;
int soundId = 1;
byte numLoops[9] = {0, 4, 2, 2, 2, 2, 2, 0, 0};
int positions[8] = {
8200, 7500, 6470, 5790,
4840, 4070, 3050, 2740
};
char newAmbientSoundName[80];
memset(newAmbientSoundName, 0, sizeof(newAmbientSoundName));
for (oldAmbientSlot1 = _soundCache; oldAmbientSlot1; oldAmbientSlot1 = oldAmbientSlot1->getNext()) {
if (oldAmbientSlot1->hasTag(kSoundTagAmbient))
break;
}
if ((_soundSlotAmbientFlag & kAmbientSoundEnabled) != 0 && (id == 69 || id == 70)) {
if ((_soundSlotAmbientFlag & kAmbientSoundSteam) != 0) {
Common::strcpy_s(newAmbientSoundName, "STEAM.SND");
_loopingSoundDuration = 0x7FFF;
} else {
if (getCharacter(kCharacterCath).characterPosition.location == kLocationOutsideTrain) {
soundId = 6;
} else if (_engine->getLogicManager()->inComp(kCharacterCath)) {
numLoops[0] = 0;
int objNum = (getCharacter(kCharacterCath).characterPosition.car == kCarGreenSleeping) ? 9 : 40;
for (int pos = 0; pos < ARRAYSIZE(positions); pos++) {
if (numLoops[0] == 1)
break;
if (_engine->getLogicManager()->inComp(kCharacterCath, getCharacter(kCharacterCath).characterPosition.car, positions[pos])) {
numLoops[0] = 1;
soundId = _engine->getLogicManager()->_doors[objNum].status == 2 ? 6 : 1;
}
objNum++;
}
} else {
switch (getCharacter(kCharacterCath).characterPosition.car) {
case kCarBaggageRear:
case kCarBaggage:
soundId = 4;
break;
case kCarKronos:
case kCarGreenSleeping:
case kCarRedSleeping:
case kCarRestaurant:
soundId = 1;
break;
case kCarCoalTender:
soundId = 5;
break;
case kCarLocomotive:
soundId = 99;
break;
case kCarVestibule:
soundId = 3;
break;
default:
soundId = 6;
break;
}
}
if (soundId != 99) {
char soundSection = (char)((rnd(UINT_MAX)) % numLoops[soundId]) + 'A';
Common::sprintf_s(newAmbientSoundName, "LOOP%d%c.SND", soundId, soundSection);
}
}
if (_scanAnySoundLoopingSection)
fileNameLen = 5;
if (!oldAmbientSlot1 || scumm_strnicmp(oldAmbientSlot1->_name2, newAmbientSoundName, fileNameLen)) {
_loopingSoundDuration = ((rnd(UINT_MAX)) % 320) + 260;
if (soundId != 99) {
if (_engine->isDemo()) {
playSoundFile(newAmbientSoundName, kSoundTypeAmbient | kSoundFlagLooped | kVolume2, kCharacterSteam, 0);
} else {
playSoundFile(newAmbientSoundName, kSoundTypeAmbient | kSoundFlagLooped | kVolume1, kCharacterSteam, 0);
}
if (oldAmbientSlot1)
oldAmbientSlot1->setFade(kVolumeNone);
oldAmbientSlot2 = _soundCache;
if (_soundCache) {
do {
if (oldAmbientSlot2->hasTag(kSoundTagAmbient))
break;
oldAmbientSlot2 = oldAmbientSlot2->getNext();
} while (oldAmbientSlot2);
if (oldAmbientSlot2)
oldAmbientSlot2->setFade(kVolume7);
}
}
}
}
}
void SoundManager::soundThread() {
int priority;
int maxPriority = 0;
bool loopedPlaying = false;
Slot *ambientSlot1;
Slot *ambientSlot2;
Slot *slotToDevirtualize;
Slot *cachedSlot;
Slot *next;
if (!isCopyingDataToSoundDriver()) {
ambientSlot1 = _soundCache;
_inSoundThreadFunction++;
if (_soundCache) {
do {
if (ambientSlot1->hasTag(kSoundTagAmbient))
break;
ambientSlot1 = ambientSlot1->getNext();
} while (ambientSlot1);
}
if ((_soundSlotAmbientFlag & kAmbientSoundEnabled) != 0) {
ambientSlot2 = _soundCache;
if (!_soundCache) {
loopedPlaying = true;
} else {
do {
if (ambientSlot2->hasTag(kSoundTagAmbient))
break;
ambientSlot2 = ambientSlot2->getNext();
} while (ambientSlot2);
if (!ambientSlot2 || _scanAnySoundLoopingSection || (ambientSlot1 && ambientSlot1->getTime() > _loopingSoundDuration))
loopedPlaying = true;
}
if (loopedPlaying) {
ambientAI(kAmbientLooping);
} else if (_soundAmbientFadeTime && getSoundDriver30HzCounter() >= _soundAmbientFadeTime) {
assert(ambientSlot1);
ambientSlot1->setFade(_soundAmbientFadeLevel);
_soundAmbientFadeTime = 0;
}
}
slotToDevirtualize = nullptr;
cachedSlot = _soundCache;
if (_soundCache) {
do {
next = cachedSlot->getNext();
if ((cachedSlot->getStatusFlags() & kSoundFlagMuteProcessed) != 0) {
if (cachedSlot->getSoundBuffer())
cachedSlot->releaseBuffer();
if (cachedSlot->closeArchive()) {
cachedSlot->setNumLoadedBytes(3);
}
if (_numActiveChannels < 6 && (cachedSlot->getStatusFlags() & kSoundVolumeMask) != 0) {
priority = cachedSlot->getPriority();
if (priority + (int)(cachedSlot->getStatusFlags() & kSoundVolumeMask) > maxPriority) {
slotToDevirtualize = cachedSlot;
maxPriority = (cachedSlot->getStatusFlags() & kSoundVolumeMask) + priority;
}
}
}
if (!cachedSlot->update() && (cachedSlot->getStatusFlags() & kSoundFlagKeepAfterFinish) == 0) {
if (slotToDevirtualize == cachedSlot) {
maxPriority = 0;
slotToDevirtualize = nullptr;
}
if (cachedSlot) {
if (cachedSlot == _engine->getNISManager()->getChainedSound()) {
// The original deleted the cachedSlot and probably set
// all its values to zero, which might not be the case on
// modern compilers and might instead trigger an exception
// on the NIS code...
_engine->getNISManager()->setChainedSound(nullptr);
}
delete cachedSlot;
cachedSlot = nullptr;
}
}
cachedSlot = next;
} while (next);
}
if (slotToDevirtualize)
slotToDevirtualize->devirtualize();
_scanAnySoundLoopingSection = false;
_inSoundThreadFunction--;
}
}
void SoundManager::killAllSlots() {
for (Slot *i = _soundCache; i; i = i->getNext())
i->addStatusFlag(kSoundFlagCloseRequested);
}
void SoundManager::killAllExcept(int tag1, int tag2, int tag3, int tag4, int tag5, int tag6, int tag7) {
Common::StackLock lock(*_engine->_soundMutex);
Slot *slot = _soundCache;
if (!tag2)
tag2 = tag1;
if (!tag3)
tag3 = tag1;
if (!tag4)
tag4 = tag1;
if (!tag5)
tag5 = tag1;
if (!tag6)
tag6 = tag1;
if (!tag7)
tag7 = tag1;
if (_soundCache) {
do {
int tag = slot->getTag();
if (tag1 != tag && tag2 != tag && tag3 != tag && tag4 != tag && tag5 != tag && tag6 != tag && tag7 != tag) {
slot->addStatusFlag(kSoundFlagCloseRequested);
slot->setAssociatedCharacter(0);
slot->closeArchive();
}
slot = slot->getNext();
} while (slot);
}
}
void SoundManager::saveSoundInfo(CVCRFile *file) {
Common::StackLock lock(*_engine->_soundMutex);
SaveSlot *saveSlot = new SaveSlot();
int numSounds = 0;
file->writeRLE(&_soundSlotAmbientFlag, 4, 1);
file->writeRLE(&_curSoundSlotTag, 4, 1);
for (Slot *i = _soundCache; i; i = i->_next) {
if (scumm_stricmp("NISSND?", i->_name2) && (i->_statusFlags & kSoundTypeMask) != kSoundTypeMenu)
numSounds++;
}
file->writeRLE(&numSounds, 4, 1);
for (Slot *j = _soundCache; j; j = j->_next) {
if (scumm_stricmp("NISSND?", j->_name2) && (j->_statusFlags & kSoundTypeMask) != kSoundTypeMenu) {
saveSlot->tag = j->_tag;
saveSlot->blockCount = j->_blockCount;
saveSlot->status = j->_statusFlags;
saveSlot->time = j->_time;
saveSlot->fadeDelayCounter = j->_fadeDelayCounter;
saveSlot->unusedVar = j->_unusedVar;
saveSlot->character = j->_character;
saveSlot->delayTicks = j->_delayedStartTime - _sound30HzCounter;
if (saveSlot->delayTicks > 0x8000000)
saveSlot->delayTicks = 0;
saveSlot->priority = j->_priority;
strncpy(saveSlot->name1, j->_name1, sizeof(saveSlot->name1));
strncpy(saveSlot->name2, j->_name2, sizeof(saveSlot->name2));
// I have verified that ANY possible name in here won't be longer than 12 characters,
// so it's safe to put this here, like Coverity asked to...
saveSlot->name1[15] = '\0';
saveSlot->name2[15] = '\0';
file->writeRLE(saveSlot, sizeof(SaveSlot), 1);
}
}
delete saveSlot;
}
void SoundManager::destroyAllSound() {
Slot *i;
Slot *next;
int32 waitCycles = 0;
addSoundDriverFlags(kSoundDriverClearBufferRequested);
// Wait for the driver to clear the mix buffer
for (i = _soundCache; (getSoundDriverFlags() & kSoundDriverClearBufferProcessed) == 0; waitCycles++) {
if (waitCycles >= 3000000)
break;
}
addSoundDriverFlags(kSoundDriverClearBufferProcessed);
if (_soundCache) {
do {
next = i->getNext();
i->setAssociatedCharacter(0);
if (i->getSoundBuffer())
i->releaseBuffer();
if (i) {
delete i;
i = nullptr;
}
i = next;
} while (next);
}
_engine->getSubtitleManager()->subThread();
}
void SoundManager::loadSoundInfo(CVCRFile *file, bool skipSoundLoading) {
Common::StackLock lock(*_engine->_soundMutex);
int numSounds;
SaveSlot *saveSlot = new SaveSlot();
if (skipSoundLoading) {
int skippedValue;
file->readRLE(&skippedValue, 4, 1);
file->readRLE(&skippedValue, 4, 1);
file->readRLE(&numSounds, 4, 1);
for (int j = 0; j < numSounds; j++) {
file->readRLE(saveSlot, sizeof(SaveSlot), 1);
}
} else {
file->readRLE(&_soundSlotAmbientFlag, 4, 1);
file->readRLE(&_curSoundSlotTag, 4, 1);
file->readRLE(&numSounds, 4, 1);
for (int j = 0; j < numSounds; j++) {
file->readRLE(saveSlot, sizeof(SaveSlot), 1);
// This apparently useless instruction automatically adds the saveSlot pointer to the cache
Slot *tmp = new Slot(this, saveSlot);
assert(tmp);
}
for (Slot *i = _soundCache; i; i = i->_next) {
if ((i->_statusFlags & kSoundFlagHasLinkAfter) != 0) {
Slot *cachedSlot = _soundCache;
if (_soundCache) {
while (scumm_stricmp(cachedSlot->_name2, i->_name1)) {
cachedSlot = cachedSlot->_next;
if (!cachedSlot)
break;
}
if (cachedSlot)
i->_chainedSound = cachedSlot;
}
}
}
_soundDriverFlags &= ~(kSoundDriverClearBufferRequested | kSoundDriverClearBufferProcessed);
}
delete saveSlot;
}
void SoundManager::addSlot(Slot *entry) {
Slot *cachedSlot = _soundCache;
if (_soundCache) {
if (_soundCache->getNext()) {
do {
cachedSlot = cachedSlot->getNext();
} while (cachedSlot->getNext());
}
cachedSlot->setNext(entry);
_soundCacheCount++;
} else {
_soundCacheCount++;
_soundCache = entry;
}
}
void SoundManager::removeSlot(Slot *entry) {
Slot *cachedSlot;
Slot *next;
cachedSlot = _soundCache;
if (_soundCache && entry) {
if (_soundCache == entry) {
_soundCache = _soundCache->getNext();
_soundCacheCount--;
} else {
if (_soundCache->getNext() != entry) {
do {
next = cachedSlot->getNext();
if (!next)
break;
cachedSlot = cachedSlot->getNext();
} while (next->getNext() != entry);
}
if (cachedSlot->getNext()) {
cachedSlot->setNext(cachedSlot->getNext()->getNext());
_soundCacheCount--;
}
}
}
}
void SoundManager::NISFadeOut() {
for (Slot *i = _soundCache; i; i = i->getNext()) {
i->assignDirectVolume(i->getStatusFlags() & kSoundVolumeMask);
if (i->getVolume())
i->setFade((i->getVolume() >> 1) + 1);
}
addSoundDriverFlags(kSoundDriverNISHasRequestedFade);
}
void SoundManager::NISFadeIn() {
removeSoundDriverFlags(kSoundDriverNISHasRequestedFade);
for (Slot *i = _soundCache; i; i = i->getNext()) {
if (i->getVolume()) {
if (!i->hasTag(kSoundTagNIS) && !i->hasTag(kSoundTagLink))
i->setFade(i->getVolume());
}
}
}
int SoundManager::getMasterVolume() {
int result = soundDriverGetVolume();
if (result < 0)
return 0;
if (result > 7)
return 7;
return result;
}
void SoundManager::setMasterVolume(int volume) {
int effVolume = volume;
if (volume < 0) {
effVolume = 0;
}
if (volume > 7)
effVolume = 7;
soundDriverSetVolume(effVolume);
}
} // End of namespace LastExpress

View File

@@ -0,0 +1,150 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LASTEXPRESS_SOUND_H
#define LASTEXPRESS_SOUND_H
#include "lastexpress/shared.h"
#include "common/str.h"
#include "audio/mixer.h"
#include "audio/audiostream.h"
namespace Audio {
class AudioStream;
class Mixer;
class QueuingAudioStream;
class SoundHandle;
} // namespace Audio
namespace LastExpress {
class CVCRFile;
class LastExpressEngine;
class SoundQueue;
class Slot;
class SoundManager {
friend class Slot;
friend class Logic;
friend class LogicManager;
friend class GraphicsManager;
friend class Menu;
friend class NISManager;
friend class LastExpressEngine;
public:
SoundManager(LastExpressEngine *engine);
~SoundManager();
void mix(Slot *slot, int16 *outBuf);
void mixEngine();
void mixChannels(int numChannels, int16 *buffer);
int playSoundFile(const char *sndName, int typeFlags, int character, int delay);
void startAmbient();
void startSteam(int cityIndex);
void endAmbient();
void killAmbient();
void raiseAmbient(int level, int delay);
void levelAmbient(int delay);
Slot *findSlotWho(int32 character);
Slot *findSlotName(char *name);
void ambientAI(int id);
void soundThread();
void killAllSlots();
void killAllExcept(int tag1, int tag2, int tag3, int tag4, int tag5, int tag6, int tag7);
void saveSoundInfo(CVCRFile *file);
void destroyAllSound();
void loadSoundInfo(CVCRFile *file, bool skipSoundLoading);
void addSlot(Slot *entry);
void removeSlot(Slot *entry);
void NISFadeOut();
void NISFadeIn();
int getMasterVolume();
void setMasterVolume(int volume);
// DRIVER
void soundDriverInit();
void soundDriverCopyBuffersToDevice();
int soundDriverGetVolume();
void soundDriverSetVolume(int volume);
int32 getSoundDriverTicks();
void setSoundDriverTicks(int32 value);
int32 getSoundDriver30HzCounter();
int32 getSoundDriverFlags();
void addSoundDriverFlags(int32 flags);
void removeSoundDriverFlags(int32 flags);
bool isCopyingDataToSoundDriver();
private:
LastExpressEngine *_engine;
Audio::Mixer *_mixer;
Audio::SoundHandle _channelHandle;
Audio::QueuingAudioStream *_stream;
int32 _sound30HzCounter = 0;
Slot *_soundCache = nullptr;
int32 _curSoundSlotTag = kSoundTagFirstNormal;
int32 _soundDriverFlags = kSoundDriverInitState;
Slot *_soundSlotChannels[6];
int32 _numActiveChannels = 0;
bool _scanAnySoundLoopingSection = false;
int32 _soundSlotAmbientFlag = 0;
int32 _soundAmbientFadeLevel = 0;
int32 _soundAmbientFadeTime = 0;
int32 _soundCacheCount = 0;
int32 _loopingSoundDuration = 0;
int32 _inSoundThreadFunction = 0;
bool _copyingDataToSoundDriver = false;
int16 _soundChannelsMixBuffers[6][1470];
int16 _soundMixBuffer[1470];
bool _soundEngineToggle = false;
int32 _soundDriverTicks = 0;
int32 _soundDriverVolume = 0;
uint32 _maxQueuedStreams = 0;
const char *_cities[17] = {
"EPERNAY",
"CHALONS",
"BARLEDUC",
"NANCY",
"LUNEVILL",
"AVRICOUR",
"DEUTSCHA",
"STRASBOU",
"BADENOOS",
"SALZBURG",
"ATTNANG",
"WELS",
"LINZ",
"VIENNA",
"POZSONY",
"GALANTA",
"POLICE"
};
};
} // End of namespace LastExpress
#endif // LASTEXPRESS_SOUND_H

View File

@@ -0,0 +1,404 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "lastexpress/sound/subtitle.h"
#include "lastexpress/sound/sound.h"
#include "lastexpress/data/archive.h"
#include "lastexpress/helpers.h"
#include "lastexpress/lastexpress.h"
#include "common/memstream.h"
#include "subtitle.h"
namespace LastExpress {
Subtitle::Subtitle(LastExpressEngine *engine, const char *filename, Slot *slot) {
_engine = engine;
memset(_filename, 0, sizeof(_filename));
_slot = slot;
if (_engine->getSubtitleManager()->_subtitlesQueue) {
Subtitle *i;
for (i = _engine->getSubtitleManager()->_subtitlesQueue; i->_next; i = i->_next);
i->_next = this;
} else {
_engine->getSubtitleManager()->_subtitlesQueue = this;
}
// Original bug: the _filename was 12 chars long, but sometimes
// the sound files are a couple of characters longer. This would trigger
// a truncated string warning within sprintf_s; we have raised the size
// to be able to make it work like the original did (since it used plain sprintf).
Common::sprintf_s(_filename, sizeof(_filename), "%s.SBE", filename);
HPF *archive = _engine->getArchiveManager()->openHPF(_filename);
if (archive) {
_engine->getArchiveManager()->closeHPF(archive);
if ((_engine->getSubtitleManager()->_flags & kSubFlagLoaded) == 0) {
load();
}
} else {
_status = kSubFlagStatusKilled;
}
}
Subtitle::~Subtitle() {
if (_engine->getSubtitleManager()->_subtitlesQueue) {
if (_engine->getSubtitleManager()->_subtitlesQueue == this) {
_engine->getSubtitleManager()->_subtitlesQueue = _engine->getSubtitleManager()->_subtitlesQueue->_next;
} else {
Subtitle *next;
Subtitle *queue = _engine->getSubtitleManager()->_subtitlesQueue;
if (_engine->getSubtitleManager()->_subtitlesQueue->_next == this) {
queue->_next = _next;
} else {
do {
next = queue->_next;
if (!next)
break;
queue = queue->_next;
} while (next->_next != this);
if (queue->_next == this)
queue->_next = _next;
}
}
}
if (_engine->getSubtitleManager()->_currentSubtitle == this) {
if (!_engine->shouldQuit())
_engine->getSubtitleManager()->clearSubArea();
_engine->getSubtitleManager()->_currentSubtitle = nullptr;
_engine->getSubtitleManager()->_flags = 0;
}
}
void Subtitle::load() {
HPF *archive;
archive = _engine->getArchiveManager()->openHPF(_filename);
_data = _engine->getSubtitleManager()->_subtitlesData + 1;
_engine->getSubtitleManager()->_subtitleIndex = -1;
if (archive) {
_engine->getArchiveManager()->readHPF(archive, _engine->getSubtitleManager()->_subtitlesData, archive->size);
_engine->getArchiveManager()->closeHPF(archive);
for (int i = 0; i < (archive->size * MEM_PAGE_SIZE) / 2; i++) {
_engine->getSubtitleManager()->_subtitlesData[i] = READ_LE_UINT16(&_engine->getSubtitleManager()->_subtitlesData[i]);
}
if (_engine->getSubtitleManager()->_subtitlesData[0]) {
for (int i = 0; i < _engine->getSubtitleManager()->_subtitlesData[0]; i++) {
if (!_data[1])
_data[1] = _data[_data[3] + 4 + _data[2]];
_data += _data[3] + _data[2] + 4;
}
}
_engine->getSubtitleManager()->_flags |= kSubFlagLoaded;
_engine->getSubtitleManager()->_currentSubtitle = this;
}
}
void Subtitle::update() {
int32 count = 0;
_data = _engine->getSubtitleManager()->_subtitlesData + 1;
if (_data[1] <= _slot->getTime()) {
do {
if (_engine->getSubtitleManager()->_subtitlesData[0] <= count)
break;
count++;
_data = &_data[_data[3] + 4 + _data[2]];
} while (_data[1] <= _slot->getTime());
}
if (_engine->getSubtitleManager()->_subtitlesData[0] <= count) {
_status = kSubFlagStatusKilled;
if ((_engine->getSubtitleManager()->_flags & kSubFlagDrawOnScreen) != 0)
_engine->getSubtitleManager()->clearSubArea();
} else {
if (_data[0] > _slot->getTime() || _data[1] <= _slot->getTime()) {
if ((_engine->getSubtitleManager()->_flags & kSubFlagDrawOnScreen) != 0) {
_engine->getSubtitleManager()->clearSubArea();
_engine->getSubtitleManager()->_currentSubtitle = this;
return;
}
} else if (count != _engine->getSubtitleManager()->_subtitleIndex) {
_engine->getSubtitleManager()->drawSubArea(&_data[2]);
_engine->getSubtitleManager()->_subtitleIndex = count;
_engine->getSubtitleManager()->_currentSubtitle = this;
return;
}
}
_engine->getSubtitleManager()->_currentSubtitle = this;
}
void Subtitle::kill() {
_status = kSubFlagStatusKilled;
}
SubtitleManager::SubtitleManager(LastExpressEngine *engine) {
_engine = engine;
memset(_upperLineCharWidths, 0, sizeof(_upperLineCharWidths));
memset(_lowerLineCharWidths, 0, sizeof(_lowerLineCharWidths));
memset(_upperLineChars, 0, sizeof(_upperLineChars));
memset(_lowerLineChars, 0, sizeof(_lowerLineChars));
}
SubtitleManager::~SubtitleManager() {
SAFE_DELETE(_font);
}
void SubtitleManager::initSubtitles() {
HPF *archive = _engine->getArchiveManager()->openHPF("FONT.DAT");
if (archive) {
if (_font->fontData) {
free(_font->fontData);
_font->fontData = nullptr;
}
byte *fontData = (byte *)malloc(MEM_PAGE_SIZE * archive->size);
if (fontData) {
_engine->getArchiveManager()->readHPF(archive, fontData, archive->size);
_engine->getArchiveManager()->closeHPF(archive);
Common::MemoryReadStream *fontStream = new Common::MemoryReadStream(fontData, MEM_PAGE_SIZE * archive->size, DisposeAfterUse::YES);
for (int i = 0; i < 16; i++) {
_font->palette[i] = fontStream->readUint16LE();
}
for (int i = 0; i < 256; i++) {
_font->charMap[i] = fontStream->readByte();
}
for (int i = 0; i < 256; i++) {
_font->charKerning[i] = fontStream->readByte();
}
uint32 sizeOfData = MEM_PAGE_SIZE * archive->size - (16 * sizeof(uint16) + 256 + 256);
_font->fontData = (byte *)malloc(sizeOfData);
assert(_font->fontData);
for (uint i = 0; !fontStream->eos() && i < sizeOfData; i++) {
_font->fontData[i] = fontStream->readByte();
}
delete fontStream;
} else {
_font->fontData = nullptr;
}
} else {
_font->fontData = nullptr;
}
_engine->getGraphicsManager()->modifyPalette(_font->palette, 16);
}
void SubtitleManager::storeVArea(PixMap *pixels) {
if (_engine->getGraphicsManager()->acquireSurface()) {
PixMap *screenSurface = (PixMap *)((byte *)_engine->getGraphicsManager()->_screenSurface.getPixels() + 537760);
for (int i = 38; i > 0; i--) {
for (int j = 480; j > 0; j--) {
*pixels++ = *screenSurface++;
}
screenSurface += 160;
}
_engine->getGraphicsManager()->unlockSurface();
}
}
void SubtitleManager::restoreVArea(PixMap *pixels) {
if (_engine->getGraphicsManager()->acquireSurface()) {
PixMap *screenSurface = (PixMap *)((byte *)_engine->getGraphicsManager()->_screenSurface.getPixels() + 537760);
for (int i = 38; i > 0; i--) {
for (int j = 480; j > 0; j--) {
*screenSurface++ = *pixels++;
}
screenSurface += 160;
}
_engine->getGraphicsManager()->unlockSurface();
}
}
void SubtitleManager::vSubOn() {
storeVArea(_engine->getGraphicsManager()->_subtitlesBackBuffer);
if (_font->fontData[0]) {
if (_engine->getGraphicsManager()->acquireSurface()) {
PixMap *surfaceLine1 = (PixMap *)((byte *)_engine->getGraphicsManager()->_screenSurface.getPixels() + ((640 - _upperLineXStart) & ~1) + 537600);
for (int i = 0; i < _upperLineLength; ++i) {
drawChar(surfaceLine1, _upperLineChars[i]);
surfaceLine1 += _upperLineCharWidths[i];
}
PixMap *surfaceLine2 = (PixMap *)((byte *)_engine->getGraphicsManager()->_screenSurface.getPixels() + ((640 - _lowerLineXStart) & ~1) + 563200);
for (int i = 0; i < _lowerLineLength; ++i) {
drawChar(surfaceLine2, _lowerLineChars[i]);
surfaceLine2 += _lowerLineCharWidths[i];
}
_engine->getGraphicsManager()->unlockSurface();
}
}
}
void SubtitleManager::vSubOff() {
restoreVArea(_engine->getGraphicsManager()->_subtitlesBackBuffer);
}
void SubtitleManager::clearSubArea() {
_flags &= ~kSubFlagDrawOnScreen;
_engine->getGraphicsManager()->burstBox(80, 420, 480, 38);
}
void SubtitleManager::drawChar(PixMap *destBuf, uint8 whichChar) {
byte *fontPtr = &_font->fontData[(288 * _font->charMap[whichChar] >> 1) + 2];
for (int row = 0; row < 18; row++) {
for (int col = 0; col < 8; col++) {
uint8 pixelByte = *fontPtr;
uint8 upperNibble = pixelByte >> 4;
if (upperNibble != 0) {
*destBuf = _font->palette[upperNibble];
}
destBuf++;
uint8 lowerNibble = pixelByte & 0x0F;
if (lowerNibble != 0) {
*destBuf = _font->palette[lowerNibble];
}
destBuf++;
fontPtr++;
}
destBuf += 624;
}
}
void SubtitleManager::drawSubArea(uint16 *subtitleData) {
uint16 *data = subtitleData + 2;
_upperLineLength = subtitleData[0];
_lowerLineLength = subtitleData[1];
_upperLineXStart = 0;
for (int i = 0; i < _upperLineLength; i++) {
_upperLineChars[i] = *data;
_upperLineCharWidths[i] = _font->charKerning[_upperLineChars[i]] + 1;
_upperLineXStart += _font->charKerning[_upperLineChars[i]] + 1;
data++;
}
_lowerLineXStart = 0;
for (int i = 0; i < _lowerLineLength; i++) {
_lowerLineChars[i] = *data;
_lowerLineCharWidths[i] = _font->charKerning[_lowerLineChars[i]] + 1;
_lowerLineXStart += _font->charKerning[_lowerLineChars[i]] + 1;
data++;
}
_flags |= kSubFlagDrawOnScreen;
_engine->getGraphicsManager()->burstBox(80, 420, 480, 38);
}
void SubtitleManager::subThread() {
int32 maxPriority;
Subtitle *queueElement;
Subtitle *selectedSubtitle;
Slot *slot;
int32 curPriority;
maxPriority = 0;
queueElement = _subtitlesQueue;
for (selectedSubtitle = 0; queueElement; queueElement = queueElement->_next) {
slot = queueElement->_slot;
if ((slot->getStatusFlags() & kSoundFlagPlaying) == 0 ||
(slot->getStatusFlags() & kSoundFlagMute) != 0 ||
!slot->getTime() ||
((slot->getStatusFlags() & kSoundVolumeMask) < kVolume6) ||
((_engine->getNISManager()->getNISFlag() & kNisFlagSoundFade) != 0 && slot->getPriority() < 90)) {
curPriority = 0;
} else {
curPriority = slot->getPriority() + (slot->getStatusFlags() & kSoundVolumeMask);
if (_currentSubtitle == queueElement)
curPriority += 4;
}
if (maxPriority < curPriority) {
maxPriority = curPriority;
selectedSubtitle = queueElement;
}
}
if (_currentSubtitle == selectedSubtitle) {
if (selectedSubtitle)
selectedSubtitle->update();
return;
}
if ((_flags & kSubFlagDrawOnScreen) != 0)
clearSubArea();
if (selectedSubtitle) {
selectedSubtitle->load();
if (selectedSubtitle)
selectedSubtitle->update();
}
}
} // End of namespace LastExpress

View File

@@ -0,0 +1,97 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LASTEXPRESS_SOUND_SUBTITLE_H
#define LASTEXPRESS_SOUND_SUBTITLE_H
#include "lastexpress/shared.h"
#include "lastexpress/sound/slot.h"
namespace LastExpress {
class LastExpressEngine;
class Slot;
struct FontData;
typedef uint16 PixMap;
class Subtitle {
friend class Slot;
friend class LastExpressEngine;
friend class SubtitleManager;
public:
Subtitle(LastExpressEngine *engine, const char *filename, Slot *slot);
~Subtitle();
void load();
void update();
void kill();
protected:
LastExpressEngine *_engine = nullptr;
char _filename[24]; // Originally 12 bytes, but it constantly overflows...
int _status = 0;
Slot *_slot = nullptr;
uint16 *_data = nullptr;
Subtitle *_next = nullptr;
};
class SubtitleManager {
friend class Subtitle;
friend class MemoryManager;
friend class GraphicsManager;
public:
SubtitleManager(LastExpressEngine *engine);
~SubtitleManager();
void initSubtitles();
void storeVArea(PixMap *pixels);
void restoreVArea(PixMap *pixels);
void vSubOn();
void vSubOff();
void clearSubArea();
void drawChar(PixMap *destBuf, uint8 whichChar);
void drawSubArea(uint16 *subtitleData);
void subThread();
private:
LastExpressEngine *_engine = nullptr;
uint16 *_subtitlesData = nullptr;
Subtitle *_subtitlesQueue = nullptr;
Subtitle *_currentSubtitle = nullptr;
int32 _flags = 0;
int32 _subtitleIndex = 0;
int32 _lowerLineXStart = 0;
int32 _upperLineXStart = 0;
int32 _upperLineCharWidths[60];
int32 _lowerLineCharWidths[60];
int16 _upperLineChars[60];
int16 _lowerLineChars[60];
int32 _upperLineLength = 0;
int32 _lowerLineLength = 0;
FontData *_font = nullptr;
};
} // End of namespace LastExpress
#endif // LASTEXPRESS_SOUND_SUBTITLE_H