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