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 * sound functionality
22 */
23
24 #include "tinsel/sound.h"
25
26 #include "tinsel/adpcm.h"
27 #include "tinsel/dw.h"
28 #include "tinsel/config.h"
29 #include "tinsel/music.h"
30 #include "tinsel/tinsel.h"
31 #include "tinsel/sysvar.h"
32 #include "tinsel/background.h"
33
34 #include "common/endian.h"
35 #include "common/memstream.h"
36 #include "common/system.h"
37
38 #include "audio/mixer.h"
39 #include "audio/decoders/flac.h"
40 #include "audio/decoders/mp3.h"
41 #include "audio/decoders/raw.h"
42 #include "audio/decoders/vorbis.h"
43 #include "audio/decoders/xa.h"
44
45
46 #include "gui/message.h"
47
48 namespace Tinsel {
49
50 extern LANGUAGE g_sampleLanguage;
51
52 //--------------------------- General data ----------------------------------
53
SoundManager(TinselEngine * vm)54 SoundManager::SoundManager(TinselEngine *vm) :
55 //_vm(vm), // TODO: Enable this once global _vm var is gone
56 _sampleIndex(0), _sampleIndexLen(0),
57 _soundMode(kVOCMode) {
58
59 for (int i = 0; i < kNumChannels; i++)
60 _channels[i].sampleNum = _channels[i].subSample = -1;
61 }
62
~SoundManager()63 SoundManager::~SoundManager() {
64 free(_sampleIndex);
65 }
66
67 /**
68 * Plays the specified sample through the sound driver.
69 * @param id Identifier of sample to be played
70 * @param type type of sound (voice or sfx)
71 * @param handle sound handle
72 */
73 // playSample for DiscWorld 1
playSample(int id,Audio::Mixer::SoundType type,Audio::SoundHandle * handle)74 bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
75 // Floppy version has no sample file.
76 if (!_vm->isV1CD())
77 return false;
78
79 // no sample driver?
80 if (!_vm->_mixer->isReady())
81 return false;
82
83 Channel &curChan = _channels[kChannelTinsel1];
84
85 // stop any currently playing sample
86 _vm->_mixer->stopHandle(curChan.handle);
87
88 // make sure id is in range
89 assert(id > 0 && id < _sampleIndexLen);
90
91 curChan.sampleNum = id;
92 curChan.subSample = 0;
93
94 // get file offset for this sample
95 uint32 dwSampleIndex = _sampleIndex[id];
96
97 // move to correct position in the sample file
98 _sampleStream.seek(dwSampleIndex);
99 if (_sampleStream.eos() || _sampleStream.err() || (uint32)_sampleStream.pos() != dwSampleIndex)
100 error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
101
102 // read the length of the sample
103 uint32 sampleLen = _sampleStream.readUint32LE();
104 if (_sampleStream.eos() || _sampleStream.err())
105 error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
106
107 if (TinselV1PSX) {
108 // Read the stream and create a XA ADPCM audio stream
109 Audio::AudioStream *xaStream = Audio::makeXAStream(_sampleStream.readStream(sampleLen), 44100);
110
111 // FIXME: Should set this in a different place ;)
112 _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
113 //_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
114 _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
115
116 // Play the audio stream
117 _vm->_mixer->playStream(type, &curChan.handle, xaStream);
118 } else {
119 // allocate a buffer
120 byte *sampleBuf = (byte *)malloc(sampleLen);
121 assert(sampleBuf);
122
123 // read all of the sample
124 if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen)
125 error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
126
127 // FIXME: Should set this in a different place ;)
128 _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
129 //_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
130 _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
131
132 Audio::AudioStream *sampleStream = 0;
133
134 // play it
135 switch (_soundMode) {
136 case kMP3Mode:
137 #ifdef USE_MAD
138 {
139 Common::MemoryReadStream *compressedStream =
140 new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
141 sampleStream = Audio::makeMP3Stream(compressedStream, DisposeAfterUse::YES);
142 }
143 #endif
144 break;
145 case kVorbisMode:
146 #ifdef USE_VORBIS
147 {
148 Common::MemoryReadStream *compressedStream =
149 new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
150 sampleStream = Audio::makeVorbisStream(compressedStream, DisposeAfterUse::YES);
151 }
152 #endif
153 break;
154 case kFLACMode:
155 #ifdef USE_FLAC
156 {
157 Common::MemoryReadStream *compressedStream =
158 new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
159 sampleStream = Audio::makeFLACStream(compressedStream, DisposeAfterUse::YES);
160 }
161 #endif
162 break;
163 default:
164 sampleStream = Audio::makeRawStream(sampleBuf, sampleLen, 22050, Audio::FLAG_UNSIGNED);
165 break;
166 }
167 if (sampleStream) {
168 _vm->_mixer->playStream(type, &curChan.handle, sampleStream);
169 }
170 }
171
172 if (handle)
173 *handle = curChan.handle;
174
175 return true;
176 }
177
playDW1MacMusic(Common::File & s,uint32 length)178 void SoundManager::playDW1MacMusic(Common::File &s, uint32 length) {
179 // TODO: It's a bad idea to load the music track in a buffer.
180 // We should use a SubReadStream instead, and keep midi.dat open.
181 // However, the track lengths aren't that big (about 1-4MB),
182 // so this shouldn't be a major issue.
183 byte *soundData = (byte *)malloc(length);
184 assert(soundData);
185
186 // read all of the sample
187 if (s.read(soundData, length) != length)
188 error(FILE_IS_CORRUPT, MIDI_FILE);
189
190 Common::SeekableReadStream *memStream = new Common::MemoryReadStream(soundData, length);
191
192 Audio::SoundHandle *handle = &_channels[kChannelDW1MacMusic].handle;
193
194 // Stop any previously playing music track
195 _vm->_mixer->stopHandle(*handle);
196
197 // TODO: Compression support (MP3/OGG/FLAC) for midi.dat in DW1 Mac
198 Audio::RewindableAudioStream *musicStream = Audio::makeRawStream(memStream, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
199
200 if (musicStream)
201 _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, handle, Audio::makeLoopingAudioStream(musicStream, 0));
202 }
203
204 // playSample for DiscWorld 2
playSample(int id,int sub,bool bLooped,int x,int y,int priority,Audio::Mixer::SoundType type,Audio::SoundHandle * handle)205 bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int priority,
206 Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
207
208 // no sample driver?
209 if (!_vm->_mixer->isReady())
210 return false;
211
212 Channel *curChan;
213
214 uint8 sndVol = 255;
215
216 // Sample on screen?
217 if (!offscreenChecks(x, y))
218 return false;
219
220 // If that sample is already playing, stop it
221 stopSpecSample(id, sub);
222
223 if (type == Audio::Mixer::kSpeechSoundType) {
224 curChan = &_channels[kChannelTalk];
225 } else if (type == Audio::Mixer::kSFXSoundType) {
226 uint32 oldestTime = g_system->getMillis();
227 int oldestChan = kChannelSFX;
228
229 int chan;
230 for (chan = kChannelSFX; chan < kNumChannels; chan++) {
231 if (!_vm->_mixer->isSoundHandleActive(_channels[chan].handle))
232 break;
233
234 if ((_channels[chan].lastStart < oldestTime) &&
235 (_channels[chan].priority <= priority)) {
236
237 oldestTime = _channels[chan].lastStart;
238 oldestChan = chan;
239 }
240 }
241
242 if (chan == kNumChannels) {
243 if (_channels[oldestChan].priority > priority) {
244 warning("playSample: No free channel");
245 return false;
246 }
247
248 chan = oldestChan;
249 }
250
251 if (_vm->_pcmMusic->isDimmed() && SysVar(SYS_SceneFxDimFactor))
252 sndVol = 255 - 255/SysVar(SYS_SceneFxDimFactor);
253
254 curChan = &_channels[chan];
255 } else {
256 warning("playSample: Unknown SoundType");
257 return false;
258 }
259
260 // stop any currently playing sample
261 _vm->_mixer->stopHandle(curChan->handle);
262
263 // make sure id is in range
264 assert(id > 0 && id < _sampleIndexLen);
265
266 // get file offset for this sample
267 uint32 dwSampleIndex = _sampleIndex[id];
268
269 if (dwSampleIndex == 0) {
270 warning("Tinsel2 playSample, non-existent sample %d", id);
271 return false;
272 }
273
274 // move to correct position in the sample file
275 _sampleStream.seek(dwSampleIndex);
276 if (_sampleStream.eos() || _sampleStream.err() || (uint32)_sampleStream.pos() != dwSampleIndex)
277 error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
278
279 // read the length of the sample
280 uint32 sampleLen = _sampleStream.readUint32LE();
281 if (_sampleStream.eos() || _sampleStream.err())
282 error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
283
284 if (sampleLen & 0x80000000) {
285 // Has sub samples
286
287 int32 numSubs = sampleLen & ~0x80000000;
288
289 assert(sub >= 0 && sub < numSubs);
290
291 // Skipping
292 for (int32 i = 0; i < sub; i++) {
293 sampleLen = _sampleStream.readUint32LE();
294 _sampleStream.skip(sampleLen);
295 if (_sampleStream.eos() || _sampleStream.err())
296 error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
297 }
298 sampleLen = _sampleStream.readUint32LE();
299 if (_sampleStream.eos() || _sampleStream.err())
300 error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
301 }
302
303 debugC(DEBUG_DETAILED, kTinselDebugSound, "Playing sound %d.%d, %d bytes at %d (pan %d)", id, sub, sampleLen,
304 _sampleStream.pos(), getPan(x));
305
306 // allocate a buffer
307 byte *sampleBuf = (byte *) malloc(sampleLen);
308 assert(sampleBuf);
309
310 // read all of the sample
311 if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen)
312 error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
313
314 Common::MemoryReadStream *compressedStream =
315 new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
316 Audio::AudioStream *sampleStream = 0;
317
318 switch (_soundMode) {
319 case kMP3Mode:
320 #ifdef USE_MAD
321 sampleStream = Audio::makeMP3Stream(compressedStream, DisposeAfterUse::YES);
322 #endif
323 break;
324 case kVorbisMode:
325 #ifdef USE_VORBIS
326 sampleStream = Audio::makeVorbisStream(compressedStream, DisposeAfterUse::YES);
327 #endif
328 break;
329 case kFLACMode:
330 #ifdef USE_FLAC
331 sampleStream = Audio::makeFLACStream(compressedStream, DisposeAfterUse::YES);
332 #endif
333 break;
334 default:
335 sampleStream = new Tinsel6_ADPCMStream(compressedStream, DisposeAfterUse::YES, sampleLen, 22050, 1, 24);
336 break;
337 }
338
339 // FIXME: Should set this in a different place ;)
340 _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
341 //_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
342 _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
343
344 curChan->sampleNum = id;
345 curChan->subSample = sub;
346 curChan->looped = bLooped;
347 curChan->x = x;
348 curChan->y = y;
349 curChan->priority = priority;
350 curChan->lastStart = g_system->getMillis();
351 // /---Compression----\ Milis BytesPerSecond
352 // not needed and won't work when using MP3/OGG/FLAC anyway
353 //curChan->timeDuration = (((sampleLen * 64) / 25) * 1000) / (22050 * 2);
354
355 // Play it
356 _vm->_mixer->playStream(type, &curChan->handle, sampleStream);
357
358 _vm->_mixer->setChannelVolume(curChan->handle, sndVol);
359 _vm->_mixer->setChannelBalance(curChan->handle, getPan(x));
360
361 if (handle)
362 *handle = curChan->handle;
363
364 return true;
365 }
366
367 /**
368 * Returns FALSE if sample doesn't need playing
369 */
offscreenChecks(int x,int & y)370 bool SoundManager::offscreenChecks(int x, int &y) {
371 // No action if no x specification
372 if (x == -1)
373 return true;
374
375 // convert x to offset from screen center
376 x -= PlayfieldGetCenterX(FIELD_WORLD);
377
378 if (x < -SCREEN_WIDTH || x > SCREEN_WIDTH) {
379 // A long way offscreen, ignore it
380 return false;
381 } else if (x < -SCREEN_WIDTH/2 || x > SCREEN_WIDTH/2) {
382 // Off-screen, attennuate it
383
384 y = (y > 0) ? (y / 2) : 50;
385
386 return true;
387 } else
388 return true;
389 }
390
getPan(int x)391 int8 SoundManager::getPan(int x) {
392 if (x == -1)
393 return 0;
394
395 x -= PlayfieldGetCenterX(FIELD_WORLD);
396
397 if (x == 0)
398 return 0;
399
400 if (x < 0) {
401 if (x < (-SCREEN_WIDTH / 2))
402 return -127;
403
404 x = (-x * 127) / (SCREEN_WIDTH / 2);
405
406 return 0 - x;
407 }
408
409 if (x > (SCREEN_WIDTH / 2))
410 return 127;
411
412 x = (x * 127) / (SCREEN_WIDTH / 2);
413
414 return x;
415 }
416
417 /**
418 * Returns TRUE if there is a sample for the specified sample identifier.
419 * @param id Identifier of sample to be checked
420 */
sampleExists(int id)421 bool SoundManager::sampleExists(int id) {
422 if (_vm->_mixer->isReady()) {
423 // make sure id is in range
424 if (id > 0 && id < _sampleIndexLen) {
425 // check for a sample index
426 if (_sampleIndex[id])
427 return true;
428 }
429 }
430
431 // no sample driver or no sample
432 return false;
433 }
434
435 /**
436 * Returns true if a sample is currently playing.
437 */
sampleIsPlaying()438 bool SoundManager::sampleIsPlaying() {
439 if (!TinselV2)
440 return _vm->_mixer->isSoundHandleActive(_channels[kChannelTinsel1].handle);
441
442 for (int i = 0; i < kNumChannels; i++)
443 if (_vm->_mixer->isSoundHandleActive(_channels[i].handle))
444 return true;
445
446 return false;
447 }
448
449 /**
450 * Stops any currently playing sample.
451 */
stopAllSamples()452 void SoundManager::stopAllSamples() {
453 if (!TinselV2) {
454 _vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle);
455 return;
456 }
457
458 for (int i = 0; i < kNumChannels; i++)
459 _vm->_mixer->stopHandle(_channels[i].handle);
460 }
461
stopSpecSample(int id,int sub)462 void SoundManager::stopSpecSample(int id, int sub) {
463 debugC(DEBUG_DETAILED, kTinselDebugSound, "stopSpecSample(%d, %d)", id, sub);
464
465 if (!TinselV2) {
466 if (_channels[kChannelTinsel1].sampleNum == id)
467 _vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle);
468 return;
469 }
470
471 for (int i = kChannelTalk; i < kNumChannels; i++) {
472 if ((_channels[i].sampleNum == id) && (_channels[i].subSample == sub))
473 _vm->_mixer->stopHandle(_channels[i].handle);
474 }
475 }
476
setSFXVolumes(uint8 volume)477 void SoundManager::setSFXVolumes(uint8 volume) {
478 if (!TinselV2)
479 return;
480
481 for (int i = kChannelSFX; i < kNumChannels; i++)
482 _vm->_mixer->setChannelVolume(_channels[i].handle, volume);
483 }
484
showSoundError(const char * errorMsg,const char * soundFile)485 void SoundManager::showSoundError(const char *errorMsg, const char *soundFile) {
486 Common::String msg;
487 msg = Common::String::format(errorMsg, soundFile);
488 GUI::MessageDialog dialog(msg, "OK");
489 dialog.runModal();
490
491 error("%s", msg.c_str());
492 }
493
494 /**
495 * Opens and inits all sound sample files.
496 */
openSampleFiles()497 void SoundManager::openSampleFiles() {
498 // V1 Floppy and V0 demo versions have no sample files
499 if (TinselV0 || (TinselV1 && !_vm->isV1CD()))
500 return;
501
502 TinselFile f;
503
504 if (_sampleIndex)
505 // already allocated
506 return;
507
508 // Open sample index (*.idx) in binary mode
509 if (f.open(_vm->getSampleIndex(g_sampleLanguage))) {
510 uint32 fileSize = f.size();
511 _sampleIndex = (uint32 *)malloc(fileSize);
512 if (_sampleIndex == NULL) {
513 showSoundError(NO_MEM, _vm->getSampleIndex(g_sampleLanguage));
514 return;
515 }
516
517 _sampleIndexLen = fileSize / 4; // total sample of indices (DWORDs)
518
519 // Load data
520 for (int i = 0; i < _sampleIndexLen; ++i) {
521 _sampleIndex[i] = f.readUint32LE();
522 if (f.err()) {
523 showSoundError(FILE_READ_ERROR, _vm->getSampleIndex(g_sampleLanguage));
524 }
525 }
526
527 f.close();
528
529 // Detect format of soundfile by looking at 1st sample-index
530 switch (TO_BE_32(_sampleIndex[0])) {
531 case MKTAG('M','P','3',' '):
532 debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected MP3 sound-data");
533 _soundMode = kMP3Mode;
534 break;
535 case MKTAG('O','G','G',' '):
536 debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected OGG sound-data");
537 _soundMode = kVorbisMode;
538 break;
539 case MKTAG('F','L','A','C'):
540 debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected FLAC sound-data");
541 _soundMode = kFLACMode;
542 break;
543 default:
544 debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected original sound-data");
545 break;
546 }
547
548 // Normally the 1st sample index points to nothing at all. We use it to
549 // determine if the game's sample files have been compressed, thus restore
550 // it here
551 _sampleIndex[0] = 0;
552 } else {
553 showSoundError(FILE_READ_ERROR, _vm->getSampleIndex(g_sampleLanguage));
554 }
555
556 // Open sample file (*.smp) in binary mode
557 if (!_sampleStream.open(_vm->getSampleFile(g_sampleLanguage))) {
558 showSoundError(FILE_READ_ERROR, _vm->getSampleFile(g_sampleLanguage));
559 }
560 }
561
closeSampleStream()562 void SoundManager::closeSampleStream() {
563 _sampleStream.close();
564 free(_sampleIndex);
565 _sampleIndex = 0;
566 _sampleIndexLen = 0;
567 }
568
569 } // End of namespace Tinsel
570