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  * Additional copyright for this file:
8  * Copyright (C) 1994-1998 Revolution Software Ltd.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24 
25 // ---------------------------------------------------------------------------
26 //								BROKEN SWORD 2
27 //
28 //	SOUND.CPP	Contains the sound engine, fx & music functions
29 //			Some very 'sound' code in here ;)
30 //
31 //	(16Dec96 JEL)
32 //
33 // ---------------------------------------------------------------------------
34 
35 
36 #include "common/file.h"
37 #include "common/memstream.h"
38 #include "common/system.h"
39 #include "common/textconsole.h"
40 
41 #include "sword2/sword2.h"
42 #include "sword2/defs.h"
43 #include "sword2/header.h"
44 #include "sword2/console.h"
45 #include "sword2/logic.h"
46 #include "sword2/resman.h"
47 #include "sword2/sound.h"
48 
49 #include "audio/decoders/wave.h"
50 #include "audio/decoders/xa.h"
51 
52 #define Debug_Printf _vm->_debugger->debugPrintf
53 
54 namespace Sword2 {
55 
Sound(Sword2Engine * vm)56 Sound::Sound(Sword2Engine *vm) {
57 	int i;
58 
59 	_vm = vm;
60 
61 	for (i = 0; i < FXQ_LENGTH; i++)
62 		_fxQueue[i].resource = 0;
63 
64 	for (i = 0; i < MAXMUS; i++) {
65 		_music[i] = NULL;
66 
67 		_musicFile[i].idxTab = NULL;
68 		_musicFile[i].idxLen = 0;
69 		_musicFile[i].fileSize = 0;
70 		_musicFile[i].fileType = 0;
71 		_musicFile[i].inUse = false;
72 
73 		_speechFile[i].idxTab = NULL;
74 		_speechFile[i].idxLen = 0;
75 		_speechFile[i].fileSize = 0;
76 		_speechFile[i].fileType = 0;
77 		_speechFile[i].inUse = false;
78 	}
79 
80 	_speechPaused = false;
81 	_musicPaused = false;
82 	_fxPaused = false;
83 
84 	_speechMuted = false;
85 	_musicMuted = false;
86 	_fxMuted = false;
87 
88 	_reverseStereo = false;
89 
90 	_loopingMusicId = 0;
91 
92 	_mixBuffer = NULL;
93 	_mixBufferLen = 0;
94 
95 	_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
96 }
97 
~Sound()98 Sound::~Sound() {
99 	_vm->_mixer->stopHandle(_mixerSoundHandle);
100 
101 	clearFxQueue(true);
102 	stopMusic(true);
103 	stopSpeech();
104 
105 	free(_mixBuffer);
106 
107 	for (int i = 0; i < MAXMUS; i++) {
108 		if (_musicFile[i].file.isOpen())
109 			_musicFile[i].file.close();
110 		if (_speechFile[i].file.isOpen())
111 			_speechFile[i].file.close();
112 
113 		free(_musicFile[i].idxTab);
114 		free(_speechFile[i].idxTab);
115 	}
116 }
117 
setReverseStereo(bool reverse)118 void Sound::setReverseStereo(bool reverse) {
119 	if (reverse != _reverseStereo) {
120 		_reverseStereo = reverse;
121 
122 		for (int i = 0; i < FXQ_LENGTH; i++) {
123 			if (!_fxQueue[i].resource)
124 				continue;
125 
126 			_fxQueue[i].pan = -_fxQueue[i].pan;
127 			_vm->_mixer->setChannelBalance(_fxQueue[i].handle, _fxQueue[i].pan);
128 		}
129 	}
130 }
131 
132 /**
133  * Stop all sounds, close their resources and clear the FX queue. This is used
134  * when going from one room to another, among other things.
135  */
136 
clearFxQueue(bool killMovieSounds)137 void Sound::clearFxQueue(bool killMovieSounds) {
138 	for (int i = 0; i < FXQ_LENGTH; i++) {
139 		if (_fxQueue[i].resource) {
140 			stopFx(i);
141 		}
142 	}
143 
144 	// We aren't just going to change rooms or anything like that, we are
145 	// killing off resources (e.g. when restoring or restarting). We need
146 	// to also kill any movie lead-in/out sounds.
147 
148 	if (killMovieSounds) {
149 		_vm->_mixer->stopHandle(_leadInHandle);
150 		_vm->_mixer->stopHandle(_leadOutHandle);
151 	}
152 }
153 
154 /**
155  * Process the FX queue. This function is called once every game cycle.
156  */
157 
processFxQueue()158 void Sound::processFxQueue() {
159 	for (int i = 0; i < FXQ_LENGTH; i++) {
160 		if (!_fxQueue[i].resource)
161 			continue;
162 
163 		switch (_fxQueue[i].type) {
164 		case FX_RANDOM:
165 			// 1 in 'delay' chance of this fx occurring
166 			if (_vm->_rnd.getRandomNumber(_fxQueue[i].delay) == 0)
167 				playFx(&_fxQueue[i]);
168 			break;
169 		case FX_SPOT:
170 			if (_fxQueue[i].delay)
171 				_fxQueue[i].delay--;
172 			else {
173 				playFx(&_fxQueue[i]);
174 				_fxQueue[i].type = FX_SPOT2;
175 			}
176 			break;
177 		case FX_LOOP:
178 			playFx(&_fxQueue[i]);
179 			_fxQueue[i].type = FX_LOOPING;
180 			break;
181 		case FX_SPOT2:
182 			// Once the FX has finished remove it from the queue.
183 			if (!_vm->_mixer->isSoundHandleActive(_fxQueue[i].handle)) {
184 				_vm->_resman->closeResource(_fxQueue[i].resource);
185 				_fxQueue[i].resource = 0;
186 			}
187 			break;
188 		case FX_LOOPING:
189 			// Once the looped FX has started we can ignore it,
190 			// but we can't close it since the WAV data is in use.
191 			break;
192 		}
193 	}
194 }
195 
196 /**
197  * This function is used by the cutscene player to play the movie lead-in/out.
198  * @param res The sound resource to play
199  * @param type Either kLeadInSound or kLeadOutSound
200  */
201 
playMovieSound(int32 res,int type)202 void Sound::playMovieSound(int32 res, int type) {
203 	Audio::SoundHandle *handle;
204 
205 	if (type == kLeadInSound)
206 		handle = &_leadInHandle;
207 	else
208 		handle = &_leadOutHandle;
209 
210 	if (_vm->_mixer->isSoundHandleActive(*handle)) {
211 		_vm->_mixer->stopHandle(*handle);
212 	}
213 
214 	byte *data = _vm->_resman->openResource(res);
215 	uint32 len = _vm->_resman->fetchLen(res);
216 
217 	assert(_vm->_resman->fetchType(data) == WAV_FILE);
218 
219 	// We want to close the resource right away, so to be safe we make a
220 	// private copy of the sound;
221 	byte *soundData = (byte *)malloc(len);
222 
223 	if (soundData) {
224 		memcpy(soundData, data, len);
225 
226 		Common::MemoryReadStream *stream = new Common::MemoryReadStream(soundData, len, DisposeAfterUse::YES);
227 
228 		// In PSX version we have nothing to skip here, as data starts
229 		// right away.
230 		if (!Sword2Engine::isPsx()) {
231 			stream->seek(ResHeader::size());
232 		}
233 
234 		Audio::RewindableAudioStream *input = 0;
235 
236 		if (Sword2Engine::isPsx()) {
237 			input = Audio::makeXAStream(stream, 11025);
238 		} else {
239 			input = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
240 		}
241 
242 		_vm->_mixer->playStream(
243 			Audio::Mixer::kMusicSoundType, handle, input,
244 			-1, Audio::Mixer::kMaxChannelVolume, 0,
245 			DisposeAfterUse::YES, false, isReverseStereo());
246 	} else {
247 		warning("Sound::playMovieSound: Could not allocate %d bytes\n", len);
248 	}
249 
250 	_vm->_resman->closeResource(res);
251 }
252 
stopMovieSounds()253 void Sound::stopMovieSounds() {
254 	if (_vm->_mixer->isSoundHandleActive(_leadInHandle)) {
255 		_vm->_mixer->stopHandle(_leadInHandle);
256 	}
257 	if (_vm->_mixer->isSoundHandleActive(_leadOutHandle)) {
258 		_vm->_mixer->stopHandle(_leadOutHandle);
259 	}
260 }
261 
262 /**
263  * Queue a sound effect for playing later.
264  * @param res the sound resource number
265  * @param type the type of sound effect
266  * @param delay when to play the sound effect
267  * @param volume the sound effect volume (0 through 16)
268  * @param pan the sound effect panning (-16 through 16)
269  */
270 
queueFx(int32 res,int32 type,int32 delay,int32 volume,int32 pan)271 void Sound::queueFx(int32 res, int32 type, int32 delay, int32 volume, int32 pan) {
272 	if (_vm->_wantSfxDebug) {
273 		const char *typeStr;
274 
275 		switch (type) {
276 		case FX_SPOT:
277 			typeStr = "SPOT";
278 			break;
279 		case FX_LOOP:
280 			typeStr = "LOOPED";
281 			break;
282 		case FX_RANDOM:
283 			typeStr = "RANDOM";
284 			break;
285 		default:
286 			typeStr = "INVALID";
287 			break;
288 		}
289 
290 		debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", _vm->_resman->fetchName(res), volume, pan, delay, typeStr);
291 	}
292 
293 	for (int i = 0; i < FXQ_LENGTH; i++) {
294 		if (!_fxQueue[i].resource) {
295 			byte *data = _vm->_resman->openResource(res);
296 
297 			// Check that we really have a WAV file here, alas this
298 			// check is useless with psx demo game, because psx audio files
299 			// are headerless and there is no way to check the type
300 			if (!(Sword2Engine::isPsx() && (_vm->_features & GF_DEMO)))
301 				assert(_vm->_resman->fetchType(data) == WAV_FILE);
302 
303 			uint32 len = _vm->_resman->fetchLen(res);
304 
305 			// Skip the header if using PC version
306 			if (!Sword2Engine::isPsx())
307 				len -= ResHeader::size();
308 
309 			if (type == FX_RANDOM) {
310 				// For spot effects and loops the delay is the
311 				// number of frames to wait. For random
312 				// effects, however, it's the average number of
313 				// seconds between playing the sound, so we
314 				// have to multiply by the frame rate.
315 				delay *= FRAMES_PER_SECOND;
316 			}
317 
318 			volume = (volume * Audio::Mixer::kMaxChannelVolume) / 16;
319 			pan = (pan * 127) / 16;
320 
321 			if (isReverseStereo())
322 				pan = -pan;
323 
324 			_fxQueue[i].resource = res;
325 
326 			if (Sword2Engine::isPsx())
327 				_fxQueue[i].data = data;
328 			else
329 				_fxQueue[i].data = data + ResHeader::size();
330 
331 			_fxQueue[i].len = len;
332 			_fxQueue[i].delay = delay;
333 			_fxQueue[i].volume = volume;
334 			_fxQueue[i].pan = pan;
335 			_fxQueue[i].type = type;
336 
337 			// Keep track of the index in the loop so that
338 			// fnStopFx() can be used later to kill this sound.
339 			// Mainly for FX_LOOP and FX_RANDOM.
340 
341 			_vm->_logic->writeVar(RESULT, i);
342 			return;
343 		}
344 	}
345 
346 	warning("No free slot in FX queue");
347 }
348 
playFx(FxQueueEntry * fx)349 int32 Sound::playFx(FxQueueEntry *fx) {
350 	return playFx(&fx->handle, fx->data, fx->len, fx->volume, fx->pan, (fx->type == FX_LOOP), Audio::Mixer::kSFXSoundType);
351 }
352 
playFx(Audio::SoundHandle * handle,byte * data,uint32 len,uint8 vol,int8 pan,bool loop,Audio::Mixer::SoundType soundType)353 int32 Sound::playFx(Audio::SoundHandle *handle, byte *data, uint32 len, uint8 vol, int8 pan, bool loop, Audio::Mixer::SoundType soundType) {
354 	if (_fxMuted)
355 		return RD_OK;
356 
357 	if (_vm->_mixer->isSoundHandleActive(*handle))
358 		return RDERR_FXALREADYOPEN;
359 
360 	Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, len);
361 	Audio::RewindableAudioStream *input = 0;
362 
363 	if (Sword2Engine::isPsx())
364 		input = Audio::makeXAStream(stream, 11025);
365 	else
366 		input = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
367 
368 	assert(input);
369 
370 	_vm->_mixer->playStream(soundType, handle,
371 	                             Audio::makeLoopingAudioStream(input, loop ? 0 : 1),
372 	                             -1, vol, pan, DisposeAfterUse::YES, false, isReverseStereo());
373 
374 	return RD_OK;
375 }
376 
377 /**
378  * This function closes a sound effect which has been previously opened for
379  * playing. Sound effects must be closed when they are finished with, otherwise
380  * you will run out of sound effect buffers.
381  * @param i the index of the sound to close
382  */
383 
stopFx(int32 i)384 int32 Sound::stopFx(int32 i) {
385 	if (!_fxQueue[i].resource)
386 		return RDERR_FXNOTOPEN;
387 
388 	_vm->_mixer->stopHandle(_fxQueue[i].handle);
389 
390 	_vm->_resman->closeResource(_fxQueue[i].resource);
391 	_fxQueue[i].resource = 0;
392 	return RD_OK;
393 }
394 
printFxQueue()395 void Sound::printFxQueue() {
396 	int freeSlots = 0;
397 
398 	for (int i = 0; i < FXQ_LENGTH; i++) {
399 		if (_fxQueue[i].resource) {
400 			const char *type;
401 
402 			switch (_fxQueue[i].type) {
403 			case FX_SPOT:
404 				type = "SPOT";
405 				break;
406 			case FX_LOOP:
407 				type = "LOOP";
408 				break;
409 			case FX_RANDOM:
410 				type = "RANDOM";
411 				break;
412 			case FX_SPOT2:
413 				type = "SPOT2";
414 				break;
415 			case FX_LOOPING:
416 				type = "LOOPING";
417 				break;
418 			default:
419 				type = "UNKNOWN";
420 				break;
421 			}
422 
423 			Debug_Printf("%d: res: %d ('%s') %s (%d) delay: %d vol: %d pan: %d\n",
424 				i, _fxQueue[i].resource,
425 				_vm->_resman->fetchName(_fxQueue[i].resource),
426 				type, _fxQueue[i].type, _fxQueue[i].delay,
427 				_fxQueue[i].volume, _fxQueue[i].pan);
428 		} else {
429 			freeSlots++;
430 		}
431 	}
432 	Debug_Printf("Free slots: %d\n", freeSlots);
433 }
434 
435 } // End of namespace Sword2
436