1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 
24 #include "common/endian.h"
25 
26 #include "common/util.h"
27 #include "common/memstream.h"
28 #include "common/textconsole.h"
29 
30 #include "sword1/sound.h"
31 #include "sword1/resman.h"
32 #include "sword1/logic.h"
33 #include "sword1/sword1.h"
34 
35 #include "audio/audiostream.h"
36 #include "audio/decoders/flac.h"
37 #include "audio/decoders/mp3.h"
38 #include "audio/decoders/raw.h"
39 #include "audio/decoders/vorbis.h"
40 #include "audio/decoders/xa.h"
41 
42 namespace Sword1 {
43 
44 #define SOUND_SPEECH_ID 1
45 #define SPEECH_FLAGS (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN)
46 
Sound(Audio::Mixer * mixer,ResMan * pResMan)47 Sound::Sound(Audio::Mixer *mixer, ResMan *pResMan)
48 	: _rnd("sword1sound") {
49 	_mixer = mixer;
50 	_resMan = pResMan;
51 	_bigEndianSpeech = false;
52 	_cowHeader = NULL;
53 	_endOfQueue = 0;
54 	_currentCowFile = 0;
55 	_speechVolL = _speechVolR = _sfxVolL = _sfxVolR = 192;
56 }
57 
~Sound()58 Sound::~Sound() {
59 	// clean up fx queue
60 	_mixer->stopAll();
61 	for (uint8 cnt = 0; cnt < _endOfQueue; cnt++)
62 		if (_fxQueue[cnt].delay == 0)
63 			_resMan->resClose(getSampleId(_fxQueue[cnt].id));
64 	_endOfQueue = 0;
65 	closeCowSystem();
66 }
67 
getSampleId(int32 fxNo)68 uint32 Sound::getSampleId(int32 fxNo) {
69 	byte cluster = _fxList[fxNo].sampleId.cluster;
70 	byte id;
71 	if (SwordEngine::_systemVars.isDemo && SwordEngine::_systemVars.platform == Common::kPlatformWindows) {
72 		id = _fxList[fxNo].sampleId.idWinDemo;
73 	} else {
74 		id = _fxList[fxNo].sampleId.idStd;
75 	}
76 	return (cluster << 24) | id;
77 }
78 
checkSpeechFileEndianness()79 void Sound::checkSpeechFileEndianness() {
80 	// Some mac versions (not all of them) use big endian wav, although
81 	// the wav header doesn't indicate it.
82 	// Use heuristic to determine endianness of speech.
83 	// The heuristic consist in computing the sum of the absolute difference for
84 	// every two consecutive samples. This is done both with a big endian and a
85 	// little endian assumption. The one with the smallest sum should be the
86 	// correct one (the sound wave is supposed to be relatively smooth).
87 	// It needs at least 1000 samples to get stable result (the code below is
88 	// using the first 2000 samples of the wav sound).
89 
90 	// Init speech file if not already done.
91 	if (!_currentCowFile) {
92 		// Open one of the speech files. It uses SwordEngine::_systemVars.currentCD
93 		// to decide which file to open, therefore if it is currently set to zero
94 		// we have to set it to either 1 or 2 (I decided to set it to 1 as this is
95 		// more likely to be the first file that will be needed).
96 		bool no_current_cd = false;
97 		if (SwordEngine::_systemVars.currentCD == 0) {
98 			SwordEngine::_systemVars.currentCD = 1;
99 			no_current_cd = true;
100 		}
101 		initCowSystem();
102 		if (no_current_cd) {
103 			// In case it fails with CD1 retry with CD2
104 			if (!_currentCowFile) {
105 				SwordEngine::_systemVars.currentCD = 2;
106 				initCowSystem();
107 			}
108 			// Reset currentCD flag
109 			SwordEngine::_systemVars.currentCD = 0;
110 		}
111 	}
112 
113 	// Testing for endianness makes sense only if using the uncompressed files.
114 	if (_cowHeader == NULL || (_cowMode != CowWave && _cowMode != CowDemo))
115 		return;
116 
117 	// I picked the sample to use randomly (I just made sure it is long enough so that there is
118 	// a fair chance of the heuristic to have a stable result and work for every language).
119 	int roomNo = _currentCowFile == 1 ? 1 : 129;
120 	int localNo = _currentCowFile == 1 ? 2 : 933;
121 	// Get the speech data and apply the heuristic
122 	uint32 locIndex = _cowHeader[roomNo] >> 2;
123 	uint32 sampleSize = _cowHeader[locIndex + (localNo * 2)];
124 	uint32 index = _cowHeader[locIndex + (localNo * 2) - 1];
125 	if (sampleSize) {
126 		uint32 size;
127 		bool leOk = false, beOk = false;
128 		// Compute average of difference between two consecutive samples for both BE and LE
129 		_bigEndianSpeech = false;
130 		int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size, &leOk);
131 		uint32 maxSamples = size > 2000 ? 2000 : size;
132 		double le_diff = endiannessHeuristicValue(data, size, maxSamples);
133 		delete[] data;
134 		_bigEndianSpeech = true;
135 		data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size, &beOk);
136 		double be_diff = endiannessHeuristicValue(data, size, maxSamples);
137 		delete [] data;
138 		// Set the big endian flag
139 		if (leOk && !beOk)
140 			_bigEndianSpeech = false;
141 		else if (beOk && !leOk)
142 			_bigEndianSpeech = true;
143 		else
144 			_bigEndianSpeech = (be_diff < le_diff);
145 		if (_bigEndianSpeech)
146 			debug(6, "Mac version: using big endian speech file");
147 		else
148 			debug(6, "Mac version: using little endian speech file");
149 		debug(8, "Speech decompression memory check: big endian = %s, little endian = %s", beOk ? "good" : "bad", leOk ? "good" : "bad");
150 		debug(8, "Speech endianness heuristic: average = %f for BE and %f for LE (%d samples)", be_diff, le_diff, maxSamples);
151 	}
152 }
153 
endiannessHeuristicValue(int16 * data,uint32 dataSize,uint32 & maxSamples)154 double Sound::endiannessHeuristicValue(int16* data, uint32 dataSize, uint32 &maxSamples) {
155 	if (!data)
156 		return 50000.; // the heuristic value for the wrong endianess is about 21000 (1/3rd of the 16 bits range)
157 
158 	double diff_sum = 0.;
159 	uint32 cpt = 0;
160 	int16 prev_value = (int16)FROM_LE_16(*((uint16 *)(data)));
161 	for (uint32 i = 1; i < dataSize && cpt < maxSamples; ++i) {
162 		int16 value = (int16)FROM_LE_16(*((uint16 *)(data + i)));
163 		if (value != prev_value) {
164 			diff_sum += fabs((double)(value - prev_value));
165 			++cpt;
166 			prev_value = value;
167 		}
168 	}
169 	if (cpt == 0)
170 		return 50000.;
171 	maxSamples = cpt;
172 	return diff_sum / cpt;
173 }
174 
175 
addToQueue(int32 fxNo)176 int Sound::addToQueue(int32 fxNo) {
177 	bool alreadyInQueue = false;
178 	for (uint8 cnt = 0; (cnt < _endOfQueue) && (!alreadyInQueue); cnt++)
179 		if (_fxQueue[cnt].id == (uint32)fxNo)
180 			alreadyInQueue = true;
181 	if (!alreadyInQueue) {
182 		if (_endOfQueue == MAX_FXQ_LENGTH) {
183 			warning("Sound queue overflow");
184 			return 0;
185 		}
186 		uint32 sampleId = getSampleId(fxNo);
187 		if ((sampleId & 0xFF) != 0xFF) {
188 			_resMan->resOpen(sampleId);
189 			_fxQueue[_endOfQueue].id = fxNo;
190 			if (_fxList[fxNo].type == FX_SPOT)
191 				_fxQueue[_endOfQueue].delay = _fxList[fxNo].delay + 1;
192 			else
193 				_fxQueue[_endOfQueue].delay = 1;
194 			_endOfQueue++;
195 			return 1;
196 		}
197 		return 0;
198 	}
199 	return 0;
200 }
201 
engine()202 void Sound::engine() {
203 	// first of all, add any random sfx to the queue...
204 	for (uint16 cnt = 0; cnt < TOTAL_FX_PER_ROOM; cnt++) {
205 		uint16 fxNo = _roomsFixedFx[Logic::_scriptVars[SCREEN]][cnt];
206 		if (fxNo) {
207 			if (_fxList[fxNo].type == FX_RANDOM) {
208 				if (_rnd.getRandomNumber(_fxList[fxNo].delay) == 0)
209 					addToQueue(fxNo);
210 			}
211 		} else
212 			break;
213 	}
214 	// now process the queue
215 	for (uint8 cnt2 = 0; cnt2 < _endOfQueue; cnt2++) {
216 		if (_fxQueue[cnt2].delay > 0) {
217 			_fxQueue[cnt2].delay--;
218 			if (_fxQueue[cnt2].delay == 0)
219 				playSample(&_fxQueue[cnt2]);
220 		} else {
221 			if (!_mixer->isSoundHandleActive(_fxQueue[cnt2].handle)) { // sound finished
222 				_resMan->resClose(getSampleId(_fxQueue[cnt2].id));
223 				if (cnt2 != _endOfQueue - 1)
224 					_fxQueue[cnt2] = _fxQueue[_endOfQueue - 1];
225 				_endOfQueue--;
226 			}
227 		}
228 	}
229 }
230 
fnStopFx(int32 fxNo)231 void Sound::fnStopFx(int32 fxNo) {
232 	_mixer->stopID(fxNo);
233 	for (uint8 cnt = 0; cnt < _endOfQueue; cnt++)
234 		if (_fxQueue[cnt].id == (uint32)fxNo) {
235 			if (!_fxQueue[cnt].delay) // sound was started
236 				_resMan->resClose(getSampleId(_fxQueue[cnt].id));
237 			if (cnt != _endOfQueue - 1)
238 				_fxQueue[cnt] = _fxQueue[_endOfQueue - 1];
239 			_endOfQueue--;
240 			return;
241 		}
242 	debug(8, "fnStopFx: id not found in queue");
243 }
244 
amISpeaking()245 bool Sound::amISpeaking() {
246 	_waveVolPos++;
247 	return _waveVolume[_waveVolPos - 1];
248 }
249 
speechFinished()250 bool Sound::speechFinished() {
251 	return !_mixer->isSoundHandleActive(_speechHandle);
252 }
253 
newScreen(uint32 screen)254 void Sound::newScreen(uint32 screen) {
255 	if (_currentCowFile != SwordEngine::_systemVars.currentCD) {
256 		if (_cowFile.isOpen())
257 			closeCowSystem();
258 		initCowSystem();
259 	}
260 
261 	// Start the room's looping sounds.
262 	for (uint16 cnt = 0; cnt < TOTAL_FX_PER_ROOM; cnt++) {
263 		uint16 fxNo = _roomsFixedFx[screen][cnt];
264 		if (fxNo) {
265 			if (_fxList[fxNo].type == FX_LOOP)
266 				addToQueue(fxNo);
267 		} else
268 			break;
269 	}
270 }
271 
quitScreen()272 void Sound::quitScreen() {
273 	// stop all running SFX
274 	while (_endOfQueue)
275 		fnStopFx(_fxQueue[0].id);
276 }
277 
playSample(QueueElement * elem)278 void Sound::playSample(QueueElement *elem) {
279 	uint8 *sampleData = (uint8 *)_resMan->fetchRes(getSampleId(elem->id));
280 	for (uint16 cnt = 0; cnt < MAX_ROOMS_PER_FX; cnt++) {
281 		if (_fxList[elem->id].roomVolList[cnt].roomNo) {
282 			if ((_fxList[elem->id].roomVolList[cnt].roomNo == (int)Logic::_scriptVars[SCREEN]) ||
283 			        (_fxList[elem->id].roomVolList[cnt].roomNo == -1)) {
284 
285 				uint8 volL = (_fxList[elem->id].roomVolList[cnt].leftVol * 10 * _sfxVolL) / 255;
286 				uint8 volR = (_fxList[elem->id].roomVolList[cnt].rightVol * 10 * _sfxVolR) / 255;
287 				int8 pan = (volR - volL) / 2;
288 				uint8 volume = (volR + volL) / 2;
289 
290 				if (SwordEngine::isPsx()) {
291 					uint32 size = READ_LE_UINT32(sampleData);
292 					Audio::AudioStream *audStream = Audio::makeLoopingAudioStream(Audio::makeXAStream(new Common::MemoryReadStream(sampleData + 4, size - 4), 11025), (_fxList[elem->id].type == FX_LOOP) ? 0 : 1);
293 					_mixer->playStream(Audio::Mixer::kSFXSoundType, &elem->handle, audStream, elem->id, volume, pan);
294 				} else {
295 					uint32 size = READ_LE_UINT32(sampleData + 0x28);
296 					uint8 flags;
297 					if (READ_LE_UINT16(sampleData + 0x22) == 16)
298 						flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
299 					else
300 						flags = Audio::FLAG_UNSIGNED;
301 					if (READ_LE_UINT16(sampleData + 0x16) == 2)
302 						flags |= Audio::FLAG_STEREO;
303 					Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
304 					                                 Audio::makeRawStream(sampleData + 0x2C, size, 11025, flags, DisposeAfterUse::NO),
305 					                                 (_fxList[elem->id].type == FX_LOOP) ? 0 : 1);
306 					_mixer->playStream(Audio::Mixer::kSFXSoundType, &elem->handle, stream, elem->id, volume, pan);
307 				}
308 			}
309 		} else
310 			break;
311 	}
312 }
313 
startSpeech(uint16 roomNo,uint16 localNo)314 bool Sound::startSpeech(uint16 roomNo, uint16 localNo) {
315 	if (_cowHeader == NULL) {
316 		warning("Sound::startSpeech: COW file isn't open");
317 		return false;
318 	}
319 
320 	uint32 locIndex = 0xFFFFFFFF;
321 	uint32 sampleSize = 0;
322 	uint32 index = 0;
323 
324 	if (_cowMode == CowPSX) {
325 		Common::File file;
326 		uint16 i;
327 
328 		if (!file.open("speech.lis")) {
329 			warning("Could not open speech.lis");
330 			return false;
331 		}
332 
333 		for (i = 0; !file.eos() && !file.err(); i++)
334 			if (file.readUint16LE() == roomNo) {
335 				locIndex = i;
336 				break;
337 			}
338 		file.close();
339 
340 		if (locIndex == 0xFFFFFFFF) {
341 			warning("Could not find room %d in speech.lis", roomNo);
342 			return false;
343 		}
344 
345 		if (!file.open("speech.inf")) {
346 			warning("Could not open speech.inf");
347 			return false;
348 		}
349 
350 		uint16 numRooms = file.readUint16LE(); // Read number of rooms referenced in this file
351 
352 		file.seek(locIndex * 4 + 2); // 4 bytes per room, skip first 2 bytes
353 
354 		uint16 numLines = file.readUint16LE();
355 		uint16 roomOffset = file.readUint16LE();
356 
357 		file.seek(2 + numRooms * 4 + roomOffset * 2); // The offset is in terms of uint16's, so multiply by 2. Skip the room indexes too.
358 
359 		locIndex = 0xFFFFFFFF;
360 
361 		for (i = 0; i < numLines; i++)
362 			if (file.readUint16LE() == localNo) {
363 				locIndex = i;
364 				break;
365 			}
366 
367 		if (locIndex == 0xFFFFFFFF) {
368 			warning("Could not find local number %d in room %d in speech.inf", roomNo, localNo);
369 			return false;
370 		}
371 
372 		file.close();
373 
374 		index = _cowHeader[(roomOffset + locIndex) * 2];
375 		sampleSize = _cowHeader[(roomOffset + locIndex) * 2 + 1];
376 	} else {
377 		locIndex = _cowHeader[roomNo] >> 2;
378 		sampleSize = _cowHeader[locIndex + (localNo * 2)];
379 		index = _cowHeader[locIndex + (localNo * 2) - 1];
380 	}
381 
382 	debug(6, "startSpeech(%d, %d): locIndex %d, sampleSize %d, index %d", roomNo, localNo, locIndex, sampleSize, index);
383 
384 	Audio::AudioStream *stream = 0;
385 
386 	if (sampleSize) {
387 		uint8 speechVol = (_speechVolR + _speechVolL) / 2;
388 		int8 speechPan = (_speechVolR - _speechVolL) / 2;
389 		if ((_cowMode == CowWave) || (_cowMode == CowDemo)) {
390 			uint32 size;
391 			int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size);
392 			if (data) {
393 				stream = Audio::makeRawStream((byte *)data, size, 11025, SPEECH_FLAGS);
394 				_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
395 			}
396 		} else if (_cowMode == CowPSX && sampleSize != 0xffffffff) {
397 			_cowFile.seek(index * 2048);
398 			Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
399 			assert(tmp);
400 			stream = Audio::makeXAStream(tmp, 11025);
401 			_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
402 			// with compressed audio, we can't calculate the wave volume.
403 			// so default to talking.
404 			for (int cnt = 0; cnt < 480; cnt++)
405 				_waveVolume[cnt] = true;
406 			_waveVolPos = 0;
407 		}
408 #ifdef USE_FLAC
409 		else if (_cowMode == CowFLAC) {
410 			_cowFile.seek(index);
411 			Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
412 			assert(tmp);
413 			stream = Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
414 			_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
415 			// with compressed audio, we can't calculate the wave volume.
416 			// so default to talking.
417 			for (int cnt = 0; cnt < 480; cnt++)
418 				_waveVolume[cnt] = true;
419 			_waveVolPos = 0;
420 		}
421 #endif
422 #ifdef USE_VORBIS
423 		else if (_cowMode == CowVorbis) {
424 			_cowFile.seek(index);
425 			Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
426 			assert(tmp);
427 			stream = Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
428 			_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
429 			// with compressed audio, we can't calculate the wave volume.
430 			// so default to talking.
431 			for (int cnt = 0; cnt < 480; cnt++)
432 				_waveVolume[cnt] = true;
433 			_waveVolPos = 0;
434 		}
435 #endif
436 #ifdef USE_MAD
437 		else if (_cowMode == CowMP3) {
438 			_cowFile.seek(index);
439 			Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
440 			assert(tmp);
441 			stream = Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
442 			_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
443 			// with compressed audio, we can't calculate the wave volume.
444 			// so default to talking.
445 			for (int cnt = 0; cnt < 480; cnt++)
446 				_waveVolume[cnt] = true;
447 			_waveVolPos = 0;
448 		}
449 #endif
450 		return true;
451 	} else
452 		return false;
453 }
454 
uncompressSpeech(uint32 index,uint32 cSize,uint32 * size,bool * ok)455 int16 *Sound::uncompressSpeech(uint32 index, uint32 cSize, uint32 *size, bool* ok) {
456 	uint8 *fBuf = (uint8 *)malloc(cSize);
457 	_cowFile.seek(index);
458 	_cowFile.read(fBuf, cSize);
459 	uint32 headerPos = 0;
460 
461 	while ((READ_BE_UINT32(fBuf + headerPos) != 'data') && (headerPos < 100))
462 		headerPos++;
463 
464 	if (headerPos < 100) {
465 		if (ok != 0)
466 			*ok = true;
467 		int32 resSize;
468 		int16 *srcData;
469 		uint32 srcPos;
470 		int16 length;
471 		cSize /= 2;
472 		headerPos += 4; // skip 'data' tag
473 		if (_cowMode != CowDemo) {
474 			resSize = READ_LE_UINT32(fBuf + headerPos) >> 1;
475 			headerPos += 4;
476 		} else {
477 			// the demo speech files have the uncompressed size
478 			// embedded in the compressed stream *sigh*
479 			//
480 			// But not always, apparently. See bug #2182450. Is
481 			// there any way to figure out the size other than
482 			// decoding the sound in that case?
483 
484 			if (fBuf[headerPos + 1] == 0) {
485 				if (READ_LE_UINT16(fBuf + headerPos) == 1) {
486 					resSize = READ_LE_UINT16(fBuf + headerPos + 2);
487 					resSize |= READ_LE_UINT16(fBuf + headerPos + 6) << 16;
488 				} else
489 					resSize = READ_LE_UINT32(fBuf + headerPos + 2);
490 				resSize >>= 1;
491 			} else {
492 				resSize = 0;
493 				srcData = (int16 *)fBuf;
494 				srcPos = headerPos >> 1;
495 				while (srcPos < cSize) {
496 					length = (int16)READ_LE_UINT16(srcData + srcPos);
497 					srcPos++;
498 					if (length < 0) {
499 						resSize -= length;
500 						srcPos++;
501 					} else {
502 						resSize += length;
503 						srcPos += length;
504 					}
505 				}
506 			}
507 		}
508 		assert(!(headerPos & 1));
509 		srcData = (int16 *)fBuf;
510 		srcPos = headerPos >> 1;
511 		uint32 dstPos = 0;
512 		int16 *dstData = (int16 *)malloc(resSize * 2);
513 		int32 samplesLeft = resSize;
514 		while (srcPos < cSize && samplesLeft > 0) {
515 			length = (int16)(_bigEndianSpeech ? READ_BE_UINT16(srcData + srcPos) : READ_LE_UINT16(srcData + srcPos));
516 			srcPos++;
517 			if (length < 0) {
518 				length = -length;
519 				if (length > samplesLeft) {
520 					length = samplesLeft;
521 					if (ok != 0)
522 						*ok = false;
523 				}
524 				int16 value;
525 				if (_bigEndianSpeech) {
526 					value = (int16)SWAP_BYTES_16(*((uint16 *)(srcData + srcPos)));
527 				} else {
528 					value = srcData[srcPos];
529 				}
530 				for (uint16 cnt = 0; cnt < (uint16)length; cnt++)
531 					dstData[dstPos++] = value;
532 				srcPos++;
533 			} else {
534 				if (length > samplesLeft) {
535 					length = samplesLeft;
536 					if (ok != 0)
537 						*ok = false;
538 				}
539 				if (_bigEndianSpeech) {
540 					for (uint16 cnt = 0; cnt < (uint16)length; cnt++)
541 						dstData[dstPos++] = (int16)SWAP_BYTES_16(*((uint16 *)(srcData + (srcPos++))));
542 				} else {
543 					memcpy(dstData + dstPos, srcData + srcPos, length * 2);
544 					dstPos += length;
545 					srcPos += length;
546 				}
547 			}
548 			samplesLeft -= length;
549 		}
550 		if (samplesLeft > 0) {
551 			memset(dstData + dstPos, 0, samplesLeft * 2);
552 			if (ok != 0)
553 				*ok = false;
554 		}
555 		if (_cowMode == CowDemo) // demo has wave output size embedded in the compressed data
556 			*(uint32 *)dstData = 0;
557 		free(fBuf);
558 		*size = resSize * 2;
559 		calcWaveVolume(dstData, resSize);
560 		return dstData;
561 	} else {
562 		if (ok != 0)
563 			*ok = false;
564 		free(fBuf);
565 		warning("Sound::uncompressSpeech(): DATA tag not found in wave header");
566 		*size = 0;
567 		return NULL;
568 	}
569 }
570 
calcWaveVolume(int16 * data,uint32 length)571 void Sound::calcWaveVolume(int16 *data, uint32 length) {
572 	int16 *blkPos = data + 918;
573 	uint32 cnt;
574 	for (cnt = 0; cnt < WAVE_VOL_TAB_LENGTH; cnt++)
575 		_waveVolume[cnt] = false;
576 	_waveVolPos = 0;
577 	for (uint32 blkCnt = 1; blkCnt < length / 918; blkCnt++) {
578 		if (blkCnt >= WAVE_VOL_TAB_LENGTH) {
579 			warning("Wave vol tab too small");
580 			return;
581 		}
582 		int32 average = 0;
583 		for (cnt = 0; cnt < 918; cnt++)
584 			average += blkPos[cnt];
585 		average /= 918;
586 		uint32 diff = 0;
587 		for (cnt = 0; cnt < 918; cnt++) {
588 			int16 smpDiff = *blkPos - average;
589 			diff += (uint32)ABS(smpDiff);
590 			blkPos++;
591 		}
592 		if (diff > WAVE_VOL_THRESHOLD)
593 			_waveVolume[blkCnt - 1] = true;
594 	}
595 }
596 
stopSpeech()597 void Sound::stopSpeech() {
598 	_mixer->stopID(SOUND_SPEECH_ID);
599 }
600 
initCowSystem()601 void Sound::initCowSystem() {
602 	if (SwordEngine::_systemVars.currentCD == 0)
603 		return;
604 
605 	char cowName[25];
606 	/* look for speech1/2.clu in the data dir
607 	   and speech/speech.clu (running from cd or using cd layout)
608 	*/
609 #ifdef USE_FLAC
610 	if (!_cowFile.isOpen()) {
611 		sprintf(cowName, "SPEECH%d.CLF", SwordEngine::_systemVars.currentCD);
612 		_cowFile.open(cowName);
613 		if (_cowFile.isOpen()) {
614 			debug(1, "Using FLAC compressed Speech Cluster");
615 			_cowMode = CowFLAC;
616 		}
617 	}
618 #endif
619 #ifdef USE_VORBIS
620 	if (!_cowFile.isOpen()) {
621 		sprintf(cowName, "SPEECH%d.CLV", SwordEngine::_systemVars.currentCD);
622 		_cowFile.open(cowName);
623 		if (_cowFile.isOpen()) {
624 			debug(1, "Using Vorbis compressed Speech Cluster");
625 			_cowMode = CowVorbis;
626 		}
627 	}
628 #endif
629 #ifdef USE_MAD
630 	if (!_cowFile.isOpen()) {
631 		sprintf(cowName, "SPEECH%d.CL3", SwordEngine::_systemVars.currentCD);
632 		_cowFile.open(cowName);
633 		if (_cowFile.isOpen()) {
634 			debug(1, "Using MP3 compressed Speech Cluster");
635 			_cowMode = CowMP3;
636 		}
637 	}
638 #endif
639 	if (!_cowFile.isOpen()) {
640 		sprintf(cowName, "SPEECH%d.CLU", SwordEngine::_systemVars.currentCD);
641 		_cowFile.open(cowName);
642 		if (!_cowFile.isOpen()) {
643 			_cowFile.open("speech.clu");
644 		}
645 		debug(1, "Using uncompressed Speech Cluster");
646 		_cowMode = CowWave;
647 	}
648 
649 	if (SwordEngine::isPsx()) {
650 		// There's only one file on the PSX, so set it to the current disc.
651 		_currentCowFile = SwordEngine::_systemVars.currentCD;
652 		if (!_cowFile.isOpen()) {
653 			if (!_cowFile.open("speech.dat"))
654 				error("Could not open speech.dat");
655 			_cowMode = CowPSX;
656 		}
657 	}
658 
659 	if (!_cowFile.isOpen())
660 		_cowFile.open("speech.clu");
661 
662 	if (!_cowFile.isOpen()) {
663 		_cowFile.open("cows.mad");
664 		if (_cowFile.isOpen())
665 			_cowMode = CowDemo;
666 	}
667 
668 	if (_cowFile.isOpen()) {
669 		if (SwordEngine::isPsx()) {
670 			// Get data from the external table file
671 			Common::File tableFile;
672 			if (!tableFile.open("speech.tab"))
673 				error("Could not open speech.tab");
674 			_cowHeaderSize = tableFile.size();
675 			_cowHeader = (uint32 *)malloc(_cowHeaderSize);
676 			if (_cowHeaderSize & 3)
677 				error("Unexpected cow header size %d", _cowHeaderSize);
678 			for (uint32 cnt = 0; cnt < _cowHeaderSize / 4; cnt++)
679 				_cowHeader[cnt] = tableFile.readUint32LE();
680 		} else {
681 			_cowHeaderSize = _cowFile.readUint32LE();
682 			_cowHeader = (uint32 *)malloc(_cowHeaderSize);
683 			if (_cowHeaderSize & 3)
684 				error("Unexpected cow header size %d", _cowHeaderSize);
685 			for (uint32 cnt = 0; cnt < (_cowHeaderSize / 4) - 1; cnt++)
686 				_cowHeader[cnt] = _cowFile.readUint32LE();
687 			_currentCowFile = SwordEngine::_systemVars.currentCD;
688 		}
689 	} else
690 		warning("Sound::initCowSystem: Can't open SPEECH%d.CLU", SwordEngine::_systemVars.currentCD);
691 }
692 
closeCowSystem()693 void Sound::closeCowSystem() {
694 	_cowFile.close();
695 	free(_cowHeader);
696 	_cowHeader = NULL;
697 	_currentCowFile = 0;
698 }
699 
700 } // End of namespace Sword1
701