Initial commit
This commit is contained in:
151
engines/lastexpress/sound/driver.cpp
Normal file
151
engines/lastexpress/sound/driver.cpp
Normal 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
|
||||
729
engines/lastexpress/sound/mixer.cpp
Normal file
729
engines/lastexpress/sound/mixer.cpp
Normal 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 ¶ms = 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(¤tDataPtr[0]);
|
||||
uint32 stepSizeIndex = READ_LE_INT16(¤tDataPtr[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 = ¤tDataPtr[-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
|
||||
912
engines/lastexpress/sound/slot.cpp
Normal file
912
engines/lastexpress/sound/slot.cpp
Normal 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
|
||||
170
engines/lastexpress/sound/slot.h
Normal file
170
engines/lastexpress/sound/slot.h
Normal 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
|
||||
714
engines/lastexpress/sound/sound.cpp
Normal file
714
engines/lastexpress/sound/sound.cpp
Normal 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
|
||||
150
engines/lastexpress/sound/sound.h
Normal file
150
engines/lastexpress/sound/sound.h
Normal 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
|
||||
404
engines/lastexpress/sound/subtitle.cpp
Normal file
404
engines/lastexpress/sound/subtitle.cpp
Normal 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
|
||||
97
engines/lastexpress/sound/subtitle.h
Normal file
97
engines/lastexpress/sound/subtitle.h
Normal 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
|
||||
Reference in New Issue
Block a user