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