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