scummvm/sword2/sound.cpp

375 lines
8.6 KiB
C++
Raw Normal View History

/* Copyright (C) 1994-2004 Revolution Software Ltd
2003-07-28 01:44:38 +00:00
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*/
2003-09-20 12:43:52 +00:00
// ---------------------------------------------------------------------------
2003-07-28 01:44:38 +00:00
// BROKEN SWORD 2
//
2003-09-20 12:43:52 +00:00
// SOUND.CPP Contains the sound engine, fx & music functions
// Some very 'sound' code in here ;)
2003-07-28 01:44:38 +00:00
//
// (16Dec96 JEL)
//
2003-09-20 12:43:52 +00:00
// ---------------------------------------------------------------------------
2003-07-28 01:44:38 +00:00
#include "common/stdafx.h"
#include "common/file.h"
2003-10-28 19:51:30 +00:00
#include "sword2/sword2.h"
#include "sword2/defs.h"
2003-10-28 19:51:30 +00:00
#include "sword2/interpreter.h"
#include "sword2/logic.h"
#include "sword2/resman.h"
#include "sword2/driver/d_sound.h"
2003-07-28 01:44:38 +00:00
2003-10-04 00:52:27 +00:00
namespace Sword2 {
struct FxQueueEntry {
uint32 resource; // resource id of sample
byte *data; // pointer to WAV data
uint16 delay; // cycles to wait before playing (or 'random chance' if FX_RANDOM)
uint8 volume; // 0..16
int8 pan; // -16..16
uint8 type; // FX_SPOT, FX_RANDOM or FX_LOOP
};
// FIXME: Should be in one of the classes, I guess...
static FxQueueEntry fxQueue[FXQ_LENGTH];
/**
* Initialise the FX queue by clearing all the entries. This is only used at
* the start of the game. Later when we need to clear the queue we must also
* stop the sound and close the resource.
*/
2003-07-28 01:44:38 +00:00
void Sword2Engine::initFxQueue(void) {
for (int i = 0; i < FXQ_LENGTH; i++)
fxQueue[i].resource = 0;
}
/**
* Stop all sounds, close their resources and clear the FX queue.
*/
void Sword2Engine::clearFxQueue(void) {
for (int i = 0; i < FXQ_LENGTH; i++) {
if (fxQueue[i].resource) {
_sound->stopFx(i + 1);
_resman->closeResource(fxQueue[i].resource);
fxQueue[i].resource = 0;
}
}
2003-07-28 01:44:38 +00:00
}
/**
* Process the FX queue once every game cycle
*/
2003-07-28 01:44:38 +00:00
void Sword2Engine::processFxQueue(void) {
for (int i = 0; i < FXQ_LENGTH; i++) {
if (!fxQueue[i].resource)
2003-09-20 12:43:52 +00:00
continue;
2003-07-28 01:44:38 +00:00
switch (fxQueue[i].type) {
case FX_RANDOM:
// 1 in 'delay' chance of this fx occurring
if (_rnd.getRandomNumber(fxQueue[i].delay) == 0)
triggerFx(i);
break;
case FX_SPOT:
if (fxQueue[i].delay)
fxQueue[i].delay--;
else {
triggerFx(i);
fxQueue[i].type = FX_SPOT2;
}
break;
case FX_LOOP:
triggerFx(i);
fxQueue[i].type = FX_LOOPING;
break;
case FX_SPOT2:
// Once the FX has finished remove it from the queue.
if (!_sound->isFxPlaying(i + 1)) {
_sound->stopFx(i + 1);
_resman->closeResource(fxQueue[i].resource);
fxQueue[i].resource = 0;
}
break;
case FX_LOOPING:
// Once the looped FX has started we can ignore it,
// but we can't close it since the WAV data is in use.
break;
2003-07-28 01:44:38 +00:00
}
}
}
void Sword2Engine::triggerFx(uint8 i) {
int type;
2003-07-28 01:44:38 +00:00
if (fxQueue[i].type == FX_LOOP)
type = RDSE_FXLOOP;
else
type = RDSE_FXSPOT;
2003-07-28 01:44:38 +00:00
uint32 rv = _sound->playFx(i + 1, fxQueue[i].data, fxQueue[i].volume, fxQueue[i].pan, type);
2003-07-28 01:44:38 +00:00
if (rv)
debug(5, "SFX ERROR: playFx() returned %.8x", rv);
2003-07-28 01:44:38 +00:00
}
void Sword2Engine::killMusic(void) {
_loopingMusicId = 0; // clear the 'looping' flag
_sound->stopMusic();
}
void Sword2Engine::pauseAllSound(void) {
_sound->pauseMusic();
_sound->pauseSpeech();
_sound->pauseFx();
}
void Sword2Engine::unpauseAllSound(void) {
_sound->unpauseMusic();
_sound->unpauseSpeech();
_sound->unpauseFx();
}
int32 Logic::fnPlayFx(int32 *params) {
2003-07-28 01:44:38 +00:00
// params: 0 sample resource id
2003-09-20 12:43:52 +00:00
// 1 type (FX_SPOT, FX_RANDOM, FX_LOOP)
// 2 delay (0..65535)
// 3 volume (0..16)
// 4 pan (-16..16)
// example script:
// fnPlayFx (FXWATER, FX_LOOP, 0, 10, 15);
2003-09-20 12:43:52 +00:00
// // fx_water is just a local script flag
// fx_water = result;
// .
// .
// .
// fnStopFx (fx_water);
2003-09-20 12:43:52 +00:00
if (_vm->_wantSfxDebug) {
char type[10];
switch (params[1]) {
case FX_SPOT:
strcpy(type, "SPOT");
break;
case FX_LOOP:
strcpy(type, "LOOPED");
break;
case FX_RANDOM:
strcpy(type, "RANDOM");
break;
default:
strcpy(type, "INVALID");
break;
2003-07-28 01:44:38 +00:00
}
byte buf[NAME_LEN];
debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", _vm->fetchObjectName(params[0], buf), params[3], params[4], params[2], type);
2003-07-28 01:44:38 +00:00
}
int i;
2003-07-28 01:44:38 +00:00
// Find a free slot in the FX queue
2003-09-20 12:43:52 +00:00
for (i = 0; i < FXQ_LENGTH; i++) {
if (!fxQueue[i].resource)
break;
2003-07-28 01:44:38 +00:00
}
if (i == FXQ_LENGTH) {
warning("No free slot in FX queue");
return IR_CONT;
}
2003-07-28 01:44:38 +00:00
fxQueue[i].resource = params[0];
fxQueue[i].type = params[1];
fxQueue[i].delay = params[2];
2003-07-28 01:44:38 +00:00
if (fxQueue[i].type == FX_RANDOM) {
// For spot effects and loops the dela is the number of frames
// to wait. For random effects, however, it's the average
// number of seconds between playing the sound, so we have to
// multiply by the frame rate.
fxQueue[i].delay *= 12;
}
2003-07-28 01:44:38 +00:00
fxQueue[i].volume = params[3];
fxQueue[i].pan = params[4];
2003-09-20 12:43:52 +00:00
byte *data = _vm->_resman->openResource(params[0]);
StandardHeader *header = (StandardHeader *) data;
2003-07-28 01:44:38 +00:00
assert(header->fileType == WAV_FILE);
2003-07-28 01:44:38 +00:00
fxQueue[i].data = data + sizeof(StandardHeader);
2003-07-28 01:44:38 +00:00
// Keep track of the index in the loop so that fnStopFx() can be used
// later to kill this sound. Mainly for FX_LOOP and FX_RANDOM.
2003-07-28 01:44:38 +00:00
_scriptVars[RESULT] = i;
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
int32 Logic::fnSoundFetch(int32 *params) {
// params: 0 id of sound to fetch [guess]
2003-10-10 16:14:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
/**
* Alter the volume and pan of a currently playing FX
*/
int32 Logic::fnSetFxVolAndPan(int32 *params) {
2003-09-20 12:43:52 +00:00
// params: 0 id of fx (ie. the id returned in 'result' from
// fnPlayFx
2003-09-20 12:43:52 +00:00
// 1 new volume (0..16)
// 2 new pan (-16..16)
debug(5, "fnSetFxVolAndPan(%d, %d, %d)", params[0], params[1], params[2]);
2003-09-20 12:43:52 +00:00
_vm->_sound->setFxIdVolumePan(params[0] + 1, params[1], params[2]);
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
/**
* Alter the volume of a currently playing FX
*/
int32 Logic::fnSetFxVol(int32 *params) {
2003-09-20 12:43:52 +00:00
// params: 0 id of fx (ie. the id returned in 'result' from
// fnPlayFx
2003-09-20 12:43:52 +00:00
// 1 new volume (0..16)
_vm->_sound->setFxIdVolume(params[0] + 1, params[1]);
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
int32 Logic::fnStopFx(int32 *params) {
2003-07-28 01:44:38 +00:00
// params: 0 position in queue
int32 i = params[0];
uint32 rv = _vm->_sound->stopFx(i + 1);
2003-07-28 01:44:38 +00:00
if (rv)
debug(5, "SFX ERROR: closeFx() returned %.8x", rv);
2003-07-28 01:44:38 +00:00
// Remove from queue
if (fxQueue[i].resource) {
_vm->_resman->closeResource(fxQueue[i].resource);
fxQueue[i].resource = 0;
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
/**
* Stops all FX and clears the entire FX queue.
*/
int32 Logic::fnStopAllFx(int32 *params) {
2003-09-20 12:43:52 +00:00
// params: none
2003-07-28 01:44:38 +00:00
_vm->clearFxQueue();
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
int32 Logic::fnPrepareMusic(int32 *params) {
// params: 1 id of music to prepare [guess]
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
/**
* Start a tune playing, to play once or to loop until stopped or next one
* played.
*/
2003-07-28 01:44:38 +00:00
int32 Logic::fnPlayMusic(int32 *params) {
2003-09-20 12:43:52 +00:00
// params: 0 tune id
// 1 loop flag (0 or 1)
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
char filename[128];
bool loopFlag;
2003-09-20 12:43:52 +00:00
uint32 rv;
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
if (params[1] == FX_LOOP) {
loopFlag = true;
2003-09-20 12:43:52 +00:00
// keep a note of the id, for restarting after an
// interruption to gameplay
_vm->_loopingMusicId = params[0];
2003-09-20 12:43:52 +00:00
} else {
2003-11-08 19:47:20 +00:00
loopFlag = false;
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// don't need to restart this tune after control panel or
// restore
_vm->_loopingMusicId = 0;
2003-09-20 12:43:52 +00:00
}
2003-07-28 01:44:38 +00:00
// add the appropriate file extension & play it
if (_scriptVars[DEMO]) {
// The demo I found didn't come with any music file, but you
// could use the music from the first CD of the complete game,
// I suppose...
strcpy(filename, "music.clu");
2003-09-20 12:43:52 +00:00
} else {
File f;
sprintf(filename, "music%d.clu", _vm->_resman->whichCd());
if (f.open(filename))
f.close();
else
strcpy(filename, "music.clu");
}
2003-07-28 01:44:38 +00:00
rv = _vm->_sound->streamCompMusic(filename, params[0], loopFlag);
2003-07-28 01:44:38 +00:00
if (rv)
debug(5, "ERROR: streamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv);
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
int32 Logic::fnStopMusic(int32 *params) {
2003-07-28 01:44:38 +00:00
// params: none
_vm->_loopingMusicId = 0; // clear the 'looping' flag
_vm->_sound->stopMusic();
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
int32 Logic::fnCheckMusicPlaying(int32 *params) {
2003-09-20 12:43:52 +00:00
// params: none
2003-07-28 01:44:38 +00:00
// sets result to no. of seconds of current tune remaining
// or 0 if no music playing
2003-09-20 12:43:52 +00:00
// in seconds, rounded up to the nearest second
_scriptVars[RESULT] = _vm->_sound->musicTimeRemaining();
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
2003-10-04 00:52:27 +00:00
} // End of namespace Sword2