diff options
Diffstat (limited to 'sword2/sound.cpp')
-rw-r--r-- | sword2/sound.cpp | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/sword2/sound.cpp b/sword2/sound.cpp new file mode 100644 index 0000000000..e355d8f412 --- /dev/null +++ b/sword2/sound.cpp @@ -0,0 +1,504 @@ +/* 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 <stdio.h> + +//#include "src\driver96.h" +#include "console.h" +#include "defs.h" // for RESULT +#include "interpreter.h" +#include "protocol.h" // for FetchObjectName() for debugging FN_play_fx +#include "resman.h" +#include "sound.h" + +//-------------------------------------------------------------------------------------- +typedef struct +{ + uint32 resource; // resource id of sample + uint32 fetchId; // Id of resource in PSX CD queue. :) + 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 +} _fxq_entry; + +#define FXQ_LENGTH 32 // max number of fx in queue at once [DO NOT EXCEED 255] + +_fxq_entry fxq[FXQ_LENGTH]; + +//-------------------------------------------------------------------------------------- + +uint32 looping_music_id=0; // used to store id of tunes that loop, for save & restore +char musicDirectory[120]; + +//-------------------------------------------------------------------------------------- +// local function prototypes + +void Trigger_fx (uint8 j); + +//-------------------------------------------------------------------------------------- +// initialise the fxq by clearing all the entries + +void Init_fx_queue(void) +{ + uint8 j; + + + for (j=0; j < FXQ_LENGTH; j++) // scan the queue + { + fxq[j].resource = 0; // 0 resource means 'empty' slot + fxq[j].fetchId = 0; // Not being fetched. + } +} + +//-------------------------------------------------------------------------------------- +// process the fxq once every game cycle + +void Process_fx_queue(void) +{ + uint8 j; // assuming FXQ_LENGTH is 255 or less + + + for (j=0; j < FXQ_LENGTH; j++) // scan the queue + { + if (fxq[j].resource) // if this entry isn't empty + { + if (fxq[j].type == FX_RANDOM) // if it's type FX_RANDOM + { + if (rand()%(fxq[j].delay)==0) // 1 in 'delay' chance of this fx occurring + { + Trigger_fx(j); // play it + } + + } + else if(fxq[j].type == FX_SPOT) + { + if (fxq[j].delay) // if delay is above 0 + fxq[j].delay--; // decrement delay countdown + else // if zero delay remaining + { + Trigger_fx(j); // play it + fxq[j].type = FX_SPOT2; + } + } + else if (fxq[j].type == FX_SPOT2) + { + if (IsFxOpen(j+1)) + fxq[j].resource = 0; // Once the Fx has finished remove it from the queue. + } + } + } +} + +//-------------------------------------------------------------------------------------- +void Trigger_fx(uint8 j) // called from Process_fx_queue only +{ + uint8 *data; + int32 id; + uint32 rv; + + id = (uint32)j+1; // because 0 is not a valid id + + if (fxq[j].type == FX_SPOT) + { + data = res_man.Res_open(fxq[j].resource); // load in the sample + data += sizeof(_standardHeader); + rv = PlayFx( id, data, fxq[j].volume, fxq[j].pan, RDSE_FXSPOT ); // wav data gets copied to sound memory + res_man.Res_close(fxq[j].resource); // release the sample +// fxq[j].resource = 0; // clear spot fx from queue + } + else // random & looped fx are already loaded into sound memory by FN_play_fx() + { // - to be referenced by 'j', so pass NULL data + + if (fxq[j].type == FX_RANDOM) + rv = PlayFx( id, NULL, fxq[j].volume, fxq[j].pan, RDSE_FXSPOT ); // not looped + else // FX_LOOP + rv = PlayFx( id, NULL, fxq[j].volume, fxq[j].pan, RDSE_FXLOOP ); // looped + } + + #ifdef _DEBUG + if (rv) + Zdebug("SFX ERROR: PlayFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__); + #endif +} + +//-------------------------------------------------------------------------------------- +int32 FN_play_fx(int32 *params) // called from script only +{ + // 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: FN_play_fx (FXWATER, FX_LOOP, 0, 10, 15); + // fx_water = result; // fx_water is just a local script flag + // . + // . + // . + // FN_stop_fx (fx_water); + + uint8 j=0; + uint8 *data; + uint32 id; + uint32 rv; + + //---------------------------------- + #ifdef _DEBUG + + _standardHeader *header; + char type[10]; + + + if (wantSfxDebug) + { + 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"); + } + + Zdebug("SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", FetchObjectName(params[0]), params[3], params[4], params[2], type); + } + + #endif _DEBUG + //---------------------------------- + + while ((j < FXQ_LENGTH) && (fxq[j].resource != 0)) + j++; + + if (j==FXQ_LENGTH) + { + return (IR_CONT); +// Con_fatal_error("ERROR: Sound queue overflow in FN_play_fx() (%s line %u)",__FILE__,__LINE__); + } + else + { + fxq[j].resource = params[0]; // wav resource id + fxq[j].type = params[1]; // FX_SPOT, FX_LOOP or FX_RANDOM + + if (fxq[j].type == FX_RANDOM) // FX_RANDOM: + fxq[j].delay = params[2] * 12 + 1; // 'delay' param is the intended average no. seconds between playing this effect (+1 to avoid divide-by-zero in Process_fx_queue) + else // FX_SPOT or FX_LOOP: + fxq[j].delay = params[2]; // 'delay' is no. frames to wait before playing + + fxq[j].volume = params[3]; // 0..16 + fxq[j].pan = params[4]; // -16..16 + + + if (fxq[j].type == FX_SPOT) // spot fx + { + #ifdef _DEBUG + data = res_man.Res_open(fxq[j].resource); // "pre-load" the sample; this gets it into memory + header = (_standardHeader*)data; + if (header->fileType != WAV_FILE) + Con_fatal_error("FN_play_fx given invalid resource (%s line %u)",__FILE__,__LINE__); + #else + res_man.Res_open(fxq[j].resource); // "pre-load" the sample; this gets it into memory + #endif + res_man.Res_close(fxq[j].resource); // but then releases it to "age" out if the space is needed + } + else // random & looped fx + { + id = (uint32)j+1; // because 0 is not a valid id + + data = res_man.Res_open(fxq[j].resource); // load in the sample + + #ifdef _DEBUG + header = (_standardHeader*)data; + if (header->fileType != WAV_FILE) + Con_fatal_error("FN_play_fx given invalid resource (%s line %u)",__FILE__,__LINE__); + #endif + + data += sizeof(_standardHeader); + rv = OpenFx(id,data); // copy it to sound memory, using position in queue as 'id' + + #ifdef _DEBUG + if (rv) + Zdebug("SFX ERROR: OpenFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__); + #endif + + res_man.Res_close(fxq[j].resource); // release the sample + } + } + + + //--------------------------------------------- + // (James07uag97) + if (fxq[j].type == FX_LOOP) // looped fx + Trigger_fx(j); // play now, rather than in Process_fx_queue where it was getting played again & again! + //--------------------------------------------- + + + RESULT = j; // in case we want to call FN_stop_fx() later, to kill this fx (mainly for FX_LOOP & FX_RANDOM) + + return(IR_CONT); // continue script +} + +//-------------------------------------------------------------------------------------- +int32 FN_sound_fetch(int32 *params) +{ + return (IR_CONT); +} +//-------------------------------------------------------------------------------------- +// to alter the volume and pan of a currently playing fx +int32 FN_set_fx_vol_and_pan(int32 *params) +{ +// params 0 id of fx (ie. the id returned in 'result' from FN_play_fx +// 1 new volume (0..16) +// 2 new pan (-16..16) + +// SetFxVolumePan(int32 id, uint8 vol, uint8 pan); + SetFxVolumePan(1+params[0], params[1], params[2]); // driver fx_id is 1+<pos in queue> +// Zdebug("%d",params[2]); + + return (IR_CONT); +} +//-------------------------------------------------------------------------------------- +// to alter the volume of a currently playing fx +int32 FN_set_fx_vol(int32 *params) +{ +// params 0 id of fx (ie. the id returned in 'result' from FN_play_fx +// 1 new volume (0..16) + +// SetFxIdVolume(int32 id, uint8 vol); + SetFxIdVolume(1+params[0], params[1]); + + return (IR_CONT); +} +//-------------------------------------------------------------------------------------- +int32 FN_stop_fx(int32 *params) // called from script only +{ + // 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 ((fxq[j].type == FX_RANDOM) || (fxq[j].type == FX_LOOP)) + { + id = (uint32)j+1; // because 0 is not a valid id + rv = CloseFx(id); // stop fx & remove sample from sound memory + + #ifdef _DEBUG + if (rv) + Zdebug("SFX ERROR: CloseFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__); + #endif + } + + fxq[j].resource = 0; // remove from queue + + + return(IR_CONT); // continue script +} + +//-------------------------------------------------------------------------------------- +int32 FN_stop_all_fx(int32 *params) // called from script only +{ + // Stops all looped & random fx and clears the entire queue + // NO PARAMS + + Clear_fx_queue(); + + return(IR_CONT); // continue script +} +//-------------------------------------------------------------------------------------- +// Stops all looped & random fx and clears the entire queue + +void Clear_fx_queue(void) +{ + ClearAllFx(); // stop all fx & remove the samples from sound memory + Init_fx_queue(); // clean out the queue +} + +//-------------------------------------------------------------------------------------- + +//============================================================================= +// int32 StreamMusic(uint8 *filename, int32 loopFlag) +// +// Streams music from the file defined by filename. The loopFlag should +// be set to RDSE_FXLOOP if the music is to loop back to the start. +// Otherwise, it should be RDSE_FXSPOT. +// The return value must be checked for any problems. +// +// -------------------------------------------------------------------------- +// +// int32 PauseMusic(void) +// +// Stops the music dead in it's tracks. +// +// -------------------------------------------------------------------------- +// +// int32 UnpauseMusic(void) +// +// Re-starts the music from where it was stopped. +// +//============================================================================= +int32 FN_prepare_music(int32 *params) +{ + return (IR_CONT); +} + +//-------------------------------------------------------------------------------------- +// Start a tune playing, to play once or to loop until stopped or next one played +int32 FN_play_music(int32 *params) // updated by James on 10apr97 +{ + // params 0 tune id + // 1 loop flag (0 or 1) + + char filename[128]; + uint32 loopFlag; + uint32 rv; // drivers return value + + +// Zdebug("FN_play_music(%d)", params[0]); + + if (params[1]==FX_LOOP) // if it is to loop + { + loopFlag = RDSE_FXLOOP; + looping_music_id = params[0]; // keep a note of the id, for restarting after an interruption to gameplay + } + else // just play once + { + loopFlag = RDSE_FXSPOT; + looping_music_id = 0; // don't need to restart this tune after control panel or restore + } + + + // add the appropriate file extension & play it + + #ifdef _WEBDEMO // (James 01oct97) + sprintf(filename,"MUSIC.CLU"); + #else + sprintf(filename,"%sCLUSTERS\\MUSIC.CLU", res_man.GetCdPath()); + #endif // _WEBDEMO + + rv = StreamCompMusic(filename, params[0], loopFlag); + + #ifdef _DEBUG + if (rv) + Zdebug("ERROR: StreamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv); + #endif + +// Zdebug("FN_play_music(%d) returning", params[0]); + + return(IR_CONT); // continue script +} + +//-------------------------------------------------------------------------------------- +int32 FN_stop_music(int32 *params) // called from script only +{ + // params: none + + + looping_music_id=0; // clear the 'looping' flag + + StopMusic(); + + if (params); + + return(IR_CONT); // continue script +} +//-------------------------------------------------------------------------------------- +extern void UpdateCompSampleStreaming(void); // used in Kill_music() +//-------------------------------------------------------------------------------------- +void Kill_music(void) // James22aug97 +{ + uint8 count; + + looping_music_id=0; // clear the 'looping' flag + StopMusic(); + + // THIS BIT CAUSES THE MUSIC TO STOP INSTANTLY! + for(count=0; count<16; count++) + UpdateCompSampleStreaming(); +} +//-------------------------------------------------------------------------------------- +int32 FN_check_music_playing(int32 *params) // James (30july97) +{ + + // params: none + // sets result to no. of seconds of current tune remaining + // or 0 if no music playing + + RESULT = MusicTimeRemaining(); // in seconds, rounded up to the nearest second + + return(IR_CONT); // continue script +} +//-------------------------------------------------------------------------------------- +void PauseAllSound(void) // James25july97 +{ + uint32 rv; // for drivers return value + + rv = PauseMusic(); + if (rv != RD_OK) + Zdebug("ERROR: PauseMusic() returned %.8x in PauseAllSound()", rv); + + rv = PauseSpeech(); + if (rv != RD_OK) + Zdebug("ERROR: PauseSpeech() returned %.8x in PauseAllSound()", rv); + + rv = PauseFx(); + if (rv != RD_OK) + Zdebug("ERROR: PauseFx() returned %.8x in PauseAllSound()", rv); +} +//-------------------------------------------------------------------------------------- +void UnpauseAllSound(void) // James25july97 +{ + uint32 rv; // for drivers return value + + rv = UnpauseMusic(); + if (rv != RD_OK) + Zdebug("ERROR: UnpauseMusic() returned %.8x in UnpauseAllSound()", rv); + + rv = UnpauseSpeech(); + if (rv != RD_OK) + Zdebug("ERROR: UnpauseSpeech() returned %.8x in UnpauseAllSound()", rv); + + rv = UnpauseFx(); + if (rv != RD_OK) + Zdebug("ERROR: UnpauseFx() returned %.8x in UnpauseAllSound()", rv); +} +//-------------------------------------------------------------------------------------- + |