scummvm/sword2/sound.cpp
Torbjörn Andersson b3b0118292 The resource and memory managers are now created dynamically.
Renamed the resource manager's open/close methods openResource() and
closeResource() to avoid confusion. (It was I who originally shortened
their names to open() and close(), but I've changed my mind now.)

Moved more stuff into Sword2Engine.

svn-id: r11088
2003-11-03 07:47:42 +00:00

415 lines
10 KiB
C++

/* Copyright (C) 1994-2003 Revolution Software Ltd
*
* 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$
*/
// ---------------------------------------------------------------------------
// BROKEN SWORD 2
//
// SOUND.CPP Contains the sound engine, fx & music functions
// Some very 'sound' code in here ;)
//
// (16Dec96 JEL)
//
// ---------------------------------------------------------------------------
#include "stdafx.h"
#include "sword2/sword2.h"
#include "sword2/console.h"
#include "sword2/defs.h" // for RESULT
#include "sword2/interpreter.h"
#include "sword2/logic.h"
#include "sword2/protocol.h" // for fetchObjectName() for debugging fnPlayFx
#include "sword2/resman.h"
#include "sword2/sound.h"
#include "sword2/sword2.h"
namespace Sword2 {
// initialise the fxq by clearing all the entries
void Sword2Engine::initFxQueue(void) {
for (int i = 0; i < FXQ_LENGTH; i++) {
_fxQueue[i].resource = 0; // 0 resource means 'empty' slot
_fxQueue[i].fetchId = 0; // Not being fetched.
}
}
// process the fx queue once every game cycle
void Sword2Engine::processFxQueue(void) {
for (int i = 0; i < FXQ_LENGTH; i++) {
if (!_fxQueue[i].resource)
continue;
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_SPOT2:
// Once the Fx has finished remove it from the queue.
if (g_sound->isFxOpen(i + 1))
_fxQueue[i].resource = 0;
break;
}
}
}
// called from processFxQueue only
void Sword2Engine::triggerFx(uint8 j) {
uint8 *data;
int32 id;
uint32 rv;
id = (uint32) j + 1; // because 0 is not a valid id
if (_fxQueue[j].type == FX_SPOT) {
// load in the sample
data = res_man->openResource(_fxQueue[j].resource);
data += sizeof(_standardHeader);
// wav data gets copied to sound memory
rv = g_sound->playFx(id, data, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXSPOT);
// release the sample
res_man->closeResource(_fxQueue[j].resource);
} else {
// random & looped fx are already loaded into sound memory
// by fnPlayFx()
// - to be referenced by 'j', so pass NULL data
if (_fxQueue[j].type == FX_RANDOM) {
// Not looped
rv = g_sound->playFx(id, NULL, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXSPOT);
} else {
// Looped
rv = g_sound->playFx(id, NULL, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXLOOP);
}
}
if (rv)
debug(5, "SFX ERROR: playFx() returned %.8x", rv);
}
// Stops all looped & random fx and clears the entire queue
void Sword2Engine::clearFxQueue(void) {
// stop all fx & remove the samples from sound memory
g_sound->clearAllFx();
// clean out the queue
initFxQueue();
}
void Sword2Engine::killMusic(void) {
_loopingMusicId = 0; // clear the 'looping' flag
g_sound->stopMusic();
}
void Sword2Engine::pauseAllSound(void) {
g_sound->pauseMusic();
g_sound->pauseSpeech();
g_sound->pauseFx();
}
void Sword2Engine::unpauseAllSound(void) {
g_sound->unpauseMusic();
g_sound->unpauseSpeech();
g_sound->unpauseFx();
}
// called from script only
int32 Logic::fnPlayFx(int32 *params) {
// params: 0 sample resource id
// 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);
// // fx_water is just a local script flag
// fx_water = result;
// .
// .
// .
// fnStopFx (fx_water);
uint8 j = 0;
uint8 *data;
uint32 id;
uint32 rv;
#ifdef _SWORD2_DEBUG
_standardHeader *header;
#endif
if (g_sword2->_wantSfxDebug) {
char type[10];
switch (params[1]) { // 'type'
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");
}
debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", g_sword2->fetchObjectName(params[0]), params[3], params[4], params[2], type);
}
while (j < FXQ_LENGTH && g_sword2->_fxQueue[j].resource != 0)
j++;
if (j == FXQ_LENGTH)
return IR_CONT;
g_sword2->_fxQueue[j].resource = params[0]; // wav resource id
g_sword2->_fxQueue[j].type = params[1]; // FX_SPOT, FX_LOOP or FX_RANDOM
if (g_sword2->_fxQueue[j].type == FX_RANDOM) {
// 'delay' param is the intended average no. seconds between
// playing this effect
g_sword2->_fxQueue[j].delay = params[2] * 12;
} else {
// FX_SPOT or FX_LOOP:
// 'delay' is no. frames to wait before playing
g_sword2->_fxQueue[j].delay = params[2];
}
g_sword2->_fxQueue[j].volume = params[3]; // 0..16
g_sword2->_fxQueue[j].pan = params[4]; // -16..16
if (g_sword2->_fxQueue[j].type == FX_SPOT) {
// "pre-load" the sample; this gets it into memory
data = res_man->openResource(g_sword2->_fxQueue[j].resource);
#ifdef _SWORD2_DEBUG
header = (_standardHeader*) data;
if (header->fileType != WAV_FILE)
error("fnPlayFx given invalid resource");
#endif
// but then releases it to "age" out if the space is needed
res_man->closeResource(g_sword2->_fxQueue[j].resource);
} else {
// random & looped fx
id = (uint32) j + 1; // because 0 is not a valid id
// load in the sample
data = res_man->openResource(g_sword2->_fxQueue[j].resource);
#ifdef _SWORD2_DEBUG
header = (_standardHeader*)data;
if (header->fileType != WAV_FILE)
error("fnPlayFx given invalid resource");
#endif
data += sizeof(_standardHeader);
// copy it to sound memory, using position in queue as 'id'
rv = g_sound->openFx(id, data);
if (rv)
debug(5, "SFX ERROR: openFx() returned %.8x", rv);
// release the sample
res_man->closeResource(g_sword2->_fxQueue[j].resource);
}
if (g_sword2->_fxQueue[j].type == FX_LOOP) {
// play now, rather than in Process_fx_queue where it was
// getting played again & again!
g_sword2->triggerFx(j);
}
// in case we want to call fnStopFx() later, to kill this fx
// (mainly for FX_LOOP & FX_RANDOM)
RESULT = j;
return IR_CONT;
}
int32 Logic::fnSoundFetch(int32 *params) {
// params: 0 id of sound to fetch [guess]
return IR_CONT;
}
// to alter the volume and pan of a currently playing fx
int32 Logic::fnSetFxVolAndPan(int32 *params) {
// params: 0 id of fx (ie. the id returned in 'result' from
// fnPlayFx
// 1 new volume (0..16)
// 2 new pan (-16..16)
debug(5, "fnSetFxVolAndPan(%d, %d, %d)", params[0], params[1], params[2]);
// setFxIdVolumePan(int32 id, uint8 vol, uint8 pan);
// driver fx_id is 1 + <pos in queue>
g_sound->setFxIdVolumePan(1 + params[0], params[1], params[2]);
return IR_CONT;
}
// to alter the volume of a currently playing fx
int32 Logic::fnSetFxVol(int32 *params) {
// params: 0 id of fx (ie. the id returned in 'result' from
// fnPlayFx
// 1 new volume (0..16)
// SetFxIdVolume(int32 id, uint8 vol);
g_sound->setFxIdVolume(1 + params[0], params[1]);
return IR_CONT;
}
// called from script only
int32 Logic::fnStopFx(int32 *params) {
// params: 0 position in queue
// This will stop looped & random fx instantly, and remove the fx
// from the queue. So although it doesn't stop spot fx, it will
// remove them from the queue if they haven't yet played
uint8 j = (uint8) params[0];
uint32 id;
uint32 rv;
if (g_sword2->_fxQueue[j].type == FX_RANDOM || g_sword2->_fxQueue[j].type == FX_LOOP) {
id = (uint32) j + 1; // because 0 is not a valid id
// stop fx & remove sample from sound memory
rv = g_sound->closeFx(id);
if (rv)
debug(5, "SFX ERROR: closeFx() returned %.8x", rv);
}
// remove from queue
g_sword2->_fxQueue[j].resource = 0;
return IR_CONT;
}
// called from script only
int32 Logic::fnStopAllFx(int32 *params) {
// Stops all looped & random fx and clears the entire queue
// params: none
g_sword2->clearFxQueue();
return IR_CONT;
}
int32 Logic::fnPrepareMusic(int32 *params) {
// params: 1 id of music to prepare [guess]
return IR_CONT;
}
// Start a tune playing, to play once or to loop until stopped or next one
// played
int32 Logic::fnPlayMusic(int32 *params) {
// params: 0 tune id
// 1 loop flag (0 or 1)
char filename[128];
bool loopFlag;
uint32 rv;
if (params[1] == FX_LOOP) {
loopFlag = true;
// keep a note of the id, for restarting after an
// interruption to gameplay
g_sword2->_loopingMusicId = params[0];
} else {
loopFlag = false;
// don't need to restart this tune after control panel or
// restore
g_sword2->_loopingMusicId = 0;
}
// add the appropriate file extension & play it
if (g_sword2->_features & GF_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");
} else {
File f;
sprintf(filename, "music%d.clu", res_man->whichCd());
if (f.open(filename))
f.close();
else
strcpy(filename, "music.clu");
}
rv = g_sound->streamCompMusic(filename, params[0], loopFlag);
if (rv)
debug(5, "ERROR: streamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv);
return IR_CONT;
}
// called from script only
int32 Logic::fnStopMusic(int32 *params) {
// params: none
g_sword2->_loopingMusicId = 0; // clear the 'looping' flag
g_sound->stopMusic();
return IR_CONT;
}
int32 Logic::fnCheckMusicPlaying(int32 *params) {
// params: none
// sets result to no. of seconds of current tune remaining
// or 0 if no music playing
// in seconds, rounded up to the nearest second
RESULT = g_sound->musicTimeRemaining();
return IR_CONT;
}
} // End of namespace Sword2