From bd91c1129d5b00a1cf76d02ffc31891b26829e16 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Sun, 12 Jul 2009 18:52:38 +0000 Subject: Patch #1936137: "Speech for Mac BS1 english" svn-id: r42423 --- engines/sword1/sound.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++--- engines/sword1/sound.h | 3 ++ engines/sword1/sword1.cpp | 4 ++ 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(); -- cgit v1.2.3