aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sword1/sound.cpp99
-rw-r--r--engines/sword1/sound.h3
-rw-r--r--engines/sword1/sword1.cpp4
3 files changed, 101 insertions, 5 deletions
diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp
index 5577c66fc6..3e920c5018 100644
--- a/engines/sword1/sound.cpp
+++ b/engines/sword1/sound.cpp
@@ -51,6 +51,7 @@ Sound::Sound(const char *searchPath, Audio::Mixer *mixer, ResMan *pResMan) {
strcpy(_filePath, searchPath);
_mixer = mixer;
_resMan = pResMan;
+ _bigEndianSpeech = false;
_cowHeader = NULL;
_endOfQueue = 0;
_currentCowFile = 0;
@@ -67,6 +68,83 @@ Sound::~Sound(void) {
closeCowSystem();
}
+void Sound::checkSpeechFileEndianness() {
+ // Some mac versions (not all of them) use big endian wav, although
+ // the wav header doesn't indicate it.
+ // Use heuristic to determine endianness of speech.
+ // The heuristic consist in computing the sum of the absolute difference for
+ // every two consecutive samples. This is done both with a big endian and a
+ // little endian assumption. The one with the smallest sum should be the
+ // correct one (the sound wave is supposed to be relatively smooth).
+ // It needs at least 1000 samples to get stable result (the code below is
+ // using the first 2000 samples of the wav sound.
+
+ // Init speach file if not already done.
+ if (!_currentCowFile) {
+ // Open one of the speech file. It uses SwordEngine::_systemVars.currentCD
+ // to decide which file to open, therefore if it is currently set to zero
+ // we have to set it to either 1 or 2 (I decided to set it to 1 as this is
+ // more likely to be the first file that will be needed).
+ bool no_current_cd = false;
+ if (SwordEngine::_systemVars.currentCD == 0) {
+ SwordEngine::_systemVars.currentCD = 1;
+ no_current_cd = true;
+ }
+ initCowSystem();
+ if (no_current_cd) {
+ // In case it fails with CD1 retyr with CD2
+ if (!_currentCowFile) {
+ SwordEngine::_systemVars.currentCD = 2;
+ initCowSystem();
+ }
+ // Reset curentCD flag
+ SwordEngine::_systemVars.currentCD = 0;
+ }
+ }
+
+ // Testing for endianness makes sense only if using the nom compressed files.
+ if (_cowHeader == NULL || (_cowMode != CowWave && _cowMode != CowDemo))
+ return;
+
+ // I picked the sample to use randomly (I just made sure it is long enough so that there is
+ // a fair change of the heuristic to have a stable result and work for every languages).
+ int roomNo = _currentCowFile == 1 ? 1 : 129;
+ int localNo = _currentCowFile == 1 ? 2 : 933;
+ // Get the speech data and apply the heuristic
+ uint32 locIndex = _cowHeader[roomNo] >> 2;
+ uint32 sampleSize = _cowHeader[locIndex + (localNo * 2)];
+ uint32 index = _cowHeader[locIndex + (localNo * 2) - 1];
+ if (sampleSize) {
+ uint32 size;
+ double be_diff_sum = 0., le_diff_sum = 0.;
+ _bigEndianSpeech = false;
+ int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size);
+ // Compute average of differecen between two consecutive samples for both BE and LE
+ if (data) {
+ if (size > 4000)
+ size = 2000;
+ else
+ size /= 2;
+ int16 prev_be_value = (int16)SWAP_BYTES_16(*((uint16*)(data)));
+ for (uint32 i = 1 ; i < size ; ++i) {
+ le_diff_sum += fabs(data[i] - data[i-1]);
+ int16 be_value = (int16)SWAP_BYTES_16(*((uint16*)(data + i)));
+ be_diff_sum += fabs(be_value - prev_be_value);
+ prev_be_value = be_value;
+ }
+ delete [] data;
+ }
+ // Set the big endian flag
+ _bigEndianSpeech = (be_diff_sum < le_diff_sum);
+ if (_bigEndianSpeech)
+ debug(6, "Mac version: using big endian speech file");
+ else
+ debug(6, "Mac version: using little endian speech file");
+ debug(8, "Speech endianness heuristic: average = %f for BE and %f for LE, computed on %d samples)", be_diff_sum / (size - 1), le_diff_sum / (size - 1), size);
+ }
+}
+
+
int Sound::addToQueue(int32 fxNo) {
bool alreadyInQueue = false;
for (uint8 cnt = 0; (cnt < _endOfQueue) && (!alreadyInQueue); cnt++)
@@ -386,21 +464,32 @@ int16 *Sound::uncompressSpeech(uint32 index, uint32 cSize, uint32 *size) {
int16 *dstData = (int16*)malloc(resSize * 2);
int32 samplesLeft = resSize;
while (srcPos < cSize && samplesLeft > 0) {
- length = (int16)READ_LE_UINT16(srcData + srcPos);
+ length = (int16)(_bigEndianSpeech ? READ_BE_UINT16(srcData + srcPos) : READ_LE_UINT16(srcData + srcPos));
srcPos++;
if (length < 0) {
length = -length;
if (length > samplesLeft)
length = samplesLeft;
+ int16 value;
+ if (_bigEndianSpeech) {
+ value = (int16)SWAP_BYTES_16(*((uint16*)(srcData + srcPos)));
+ } else {
+ value = srcData[srcPos];
+ }
for (uint16 cnt = 0; cnt < (uint16)length; cnt++)
- dstData[dstPos++] = srcData[srcPos];
+ dstData[dstPos++] = value;
srcPos++;
} else {
if (length > samplesLeft)
length = samplesLeft;
- memcpy(dstData + dstPos, srcData + srcPos, length * 2);
- dstPos += length;
- srcPos += length;
+ if (_bigEndianSpeech) {
+ for (uint16 cnt = 0; cnt < (uint16)length; cnt++)
+ dstData[dstPos++] = (int16)SWAP_BYTES_16(*((uint16*)(srcData + (srcPos++))));
+ } else {
+ memcpy(dstData + dstPos, srcData + srcPos, length * 2);
+ dstPos += length;
+ srcPos += length;
+ }
}
samplesLeft -= length;
}
diff --git a/engines/sword1/sound.h b/engines/sword1/sound.h
index cdbdcdf6bb..c105d06b50 100644
--- a/engines/sword1/sound.h
+++ b/engines/sword1/sound.h
@@ -95,6 +95,8 @@ public:
void engine(void);
+ void checkSpeechFileEndianness();
+
private:
uint8 _sfxVolL, _sfxVolR, _speechVolL, _speechVolR;
void playSample(QueueElement *elem);
@@ -116,6 +118,7 @@ private:
uint8 _endOfQueue;
Audio::Mixer *_mixer;
ResMan *_resMan;
+ bool _bigEndianSpeech;
char _filePath[100];
static const char _musicList[270];
static const uint16 _roomsFixedFx[TOTAL_ROOMS][TOTAL_FX_PER_ROOM];
diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp
index 3796ceefd6..61ab835da6 100644
--- a/engines/sword1/sword1.cpp
+++ b/engines/sword1/sword1.cpp
@@ -145,6 +145,10 @@ Common::Error SwordEngine::init() {
_systemVars.playSpeech = 1;
_mouseState = 0;
+
+ // Some Mac versions use big endian for the speech files but not all of them.
+ if (_systemVars.platform == Common::kPlatformMacintosh)
+ _sound->checkSpeechFileEndianness();
_logic->initialize();
_objectMan->initialize();