scummvm/sword2/sound.cpp

487 lines
12 KiB
C++
Raw Normal View History

2003-07-28 01:44:38 +00:00
/* 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$
*/
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 <stdio.h>
2003-07-28 03:12:49 +00:00
#include "stdafx.h"
2003-07-28 01:44:38 +00:00
#include "console.h"
2003-09-20 12:43:52 +00:00
#include "defs.h" // for RESULT
2003-07-28 01:44:38 +00:00
#include "interpreter.h"
2003-09-20 12:43:52 +00:00
#include "protocol.h" // for FetchObjectName() for debugging FN_play_fx
2003-07-28 01:44:38 +00:00
#include "resman.h"
#include "sound.h"
#include "sword2.h"
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
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
2003-07-28 01:44:38 +00:00
} _fxq_entry;
2003-09-20 12:43:52 +00:00
// max number of fx in queue at once [DO NOT EXCEED 255]
#define FXQ_LENGTH 32
2003-07-28 01:44:38 +00:00
_fxq_entry fxq[FXQ_LENGTH];
2003-09-20 12:43:52 +00:00
// used to store id of tunes that loop, for save & restore
uint32 looping_music_id=0;
2003-07-28 01:44:38 +00:00
char musicDirectory[120];
2003-09-20 12:43:52 +00:00
void Trigger_fx(uint8 j);
2003-07-28 01:44:38 +00:00
// initialise the fxq by clearing all the entries
2003-09-20 12:43:52 +00:00
void Init_fx_queue(void) {
for (int j = 0; j < FXQ_LENGTH; j++) {
fxq[j].resource = 0; // 0 resource means 'empty' slot
fxq[j].fetchId = 0; // Not being fetched.
2003-07-28 01:44:38 +00:00
}
}
// process the fxq once every game cycle
2003-09-20 12:43:52 +00:00
void Process_fx_queue(void) {
for (int j = 0; j < FXQ_LENGTH; j++) {
if (!fxq[j].resource)
continue;
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
switch (fxq[j].type) {
case FX_RANDOM:
// 1 in 'delay' chance of this fx occurring
if (rand() % fxq[j].delay == 0)
Trigger_fx(j);
break;
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
case FX_SPOT:
if (fxq[j].delay)
fxq[j].delay--;
else {
Trigger_fx(j);
2003-07-28 01:44:38 +00:00
fxq[j].type = FX_SPOT2;
}
2003-09-20 12:43:52 +00:00
break;
case FX_SPOT2:
// Once the Fx has finished remove it from
// the queue.
if (g_sound->IsFxOpen(j + 1))
fxq[j].resource = 0;
break;
2003-07-28 01:44:38 +00:00
}
}
}
2003-09-20 12:43:52 +00:00
void Trigger_fx(uint8 j) { // called from Process_fx_queue only
2003-07-28 01:44:38 +00:00
uint8 *data;
int32 id;
uint32 rv;
2003-09-20 12:43:52 +00:00
id = (uint32) j + 1; // because 0 is not a valid id
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
if (fxq[j].type == FX_SPOT) {
// load in the sample
data = res_man.Res_open(fxq[j].resource);
2003-07-28 01:44:38 +00:00
data += sizeof(_standardHeader);
2003-09-20 12:43:52 +00:00
// wav data gets copied to sound memory
rv = g_sound->PlayFx(id, data, fxq[j].volume, fxq[j].pan, RDSE_FXSPOT);
// release the sample
res_man.Res_close(fxq[j].resource);
} 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) {
// Not looped
rv = g_sound->PlayFx(id, NULL, fxq[j].volume, fxq[j].pan, RDSE_FXSPOT);
} else {
// Looped
rv = g_sound->PlayFx(id, NULL, fxq[j].volume, fxq[j].pan, RDSE_FXLOOP);
}
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
#ifdef _SWORD2_DEBUG
2003-07-28 01:44:38 +00:00
if (rv)
Zdebug("SFX ERROR: PlayFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__);
2003-09-20 12:43:52 +00:00
#endif
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
int32 FN_play_fx(int32 *params) { // called from script only
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:
// FN_play_fx (FXWATER, FX_LOOP, 0, 10, 15);
// // fx_water is just a local script flag
// fx_water = result;
// .
// .
// .
// FN_stop_fx (fx_water);
uint8 j = 0;
uint8 *data;
uint32 id;
2003-07-28 01:44:38 +00:00
uint32 rv;
2003-09-20 12:43:52 +00:00
#ifdef _SWORD2_DEBUG
2003-07-28 01:44:38 +00:00
_standardHeader *header;
char type[10];
2003-09-20 12:43:52 +00:00
if (wantSfxDebug) {
2003-07-28 01:44:38 +00:00
switch (params[1]) // 'type'
{
case FX_SPOT:
2003-09-20 12:43:52 +00:00
strcpy(type, "SPOT");
2003-07-28 01:44:38 +00:00
break;
case FX_LOOP:
2003-09-20 12:43:52 +00:00
strcpy(type, "LOOPED");
2003-07-28 01:44:38 +00:00
break;
case FX_RANDOM:
2003-09-20 12:43:52 +00:00
strcpy(type, "RANDOM");
2003-07-28 01:44:38 +00:00
break;
default:
2003-09-20 12:43:52 +00:00
strcpy(type, "INVALID");
2003-07-28 01:44:38 +00:00
}
Zdebug("SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", FetchObjectName(params[0]), params[3], params[4], params[2], type);
}
2003-09-20 12:43:52 +00:00
#endif
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
while (j < FXQ_LENGTH && fxq[j].resource != 0)
2003-07-28 01:44:38 +00:00
j++;
2003-09-20 12:43:52 +00:00
if (j == FXQ_LENGTH)
return IR_CONT;
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) {
// 'delay' param is the intended average no. seconds between
// playing this effect (+1 to avoid divide-by-zero in
// Process_fx_queue)
fxq[j].delay = params[2] * 12 + 1;
} else {
// FX_SPOT or FX_LOOP:
// 'delay' is no. frames to wait before playing
fxq[j].delay = params[2];
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
fxq[j].volume = params[3]; // 0..16
fxq[j].pan = params[4]; // -16..16
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
if (fxq[j].type == FX_SPOT) {
// "pre-load" the sample; this gets it into memory
data = res_man.Res_open(fxq[j].resource);
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
#ifdef _SWORD2_DEBUG
header = (_standardHeader*) data;
if (header->fileType != WAV_FILE)
Con_fatal_error("FN_play_fx given invalid resource (%s line %u)", __FILE__, __LINE__);
#endif
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// but then releases it to "age" out if the space is needed
res_man.Res_close(fxq[j].resource);
} else {
// random & looped fx
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
id = (uint32) j + 1; // because 0 is not a valid id
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// load in the sample
data = res_man.Res_open(fxq[j].resource);
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
#ifdef _SWORD2_DEBUG
header = (_standardHeader*)data;
if (header->fileType != WAV_FILE)
Con_fatal_error("FN_play_fx given invalid resource (%s line %u)", __FILE__, __LINE__);
#endif
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
data += sizeof(_standardHeader);
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// copy it to sound memory, using position in queue as 'id'
rv = g_sound->OpenFx(id,data);
#ifdef _SWORD2_DEBUG
if (rv)
Zdebug("SFX ERROR: OpenFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__);
#endif
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// release the sample
res_man.Res_close(fxq[j].resource);
}
2003-07-28 01:44:38 +00:00
// (James07uag97)
2003-09-20 12:43:52 +00:00
if (fxq[j].type == FX_LOOP) {
// play now, rather than in Process_fx_queue where it was
// getting played again & again!
Trigger_fx(j);
}
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// in case we want to call FN_stop_fx() later, to kill this fx
// (mainly for FX_LOOP & FX_RANDOM)
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
RESULT = j;
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
int32 FN_sound_fetch(int32 *params) {
2003-07-28 01:44:38 +00:00
return (IR_CONT);
}
2003-09-20 12:43:52 +00:00
// 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)
// Zdebug("%d", params[2]);
// SetFxVolumePan(int32 id, uint8 vol, uint8 pan);
// driver fx_id is 1 + <pos in queue>
g_sound->SetFxVolumePan(1 + params[0], params[1], params[2]);
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
// 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);
g_sound->SetFxIdVolume(1 + params[0], params[1]);
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
int32 FN_stop_fx(int32 *params) { // called from script only
2003-07-28 01:44:38 +00:00
// params: 0 position in queue
2003-09-20 12:43:52 +00:00
// 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
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
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
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// stop fx & remove sample from sound memory
rv = g_sound->CloseFx(id);
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
#ifdef _SWORD2_DEBUG
2003-07-28 01:44:38 +00:00
if (rv)
Zdebug("SFX ERROR: CloseFx() returned %.8x (%s line %u)", rv, __FILE__, __LINE__);
2003-09-20 12:43:52 +00:00
#endif
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
// remove from queue
fxq[j].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
}
2003-09-20 12:43:52 +00:00
int32 FN_stop_all_fx(int32 *params) { // called from script only
2003-07-28 01:44:38 +00:00
// Stops all looped & random fx and clears the entire queue
2003-09-20 12:43:52 +00:00
// params: none
2003-07-28 01:44:38 +00:00
Clear_fx_queue();
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-07-28 01:44:38 +00:00
// Stops all looped & random fx and clears the entire queue
2003-09-20 12:43:52 +00:00
void Clear_fx_queue(void) {
// stop all fx & remove the samples from sound memory
g_sound->ClearAllFx();
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// clean out the queue
Init_fx_queue();
}
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// ===========================================================================
2003-07-28 01:44:38 +00:00
// 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.
//
2003-09-20 12:43:52 +00:00
// ---------------------------------------------------------------------------
2003-07-28 01:44:38 +00:00
//
// int32 PauseMusic(void)
//
// Stops the music dead in it's tracks.
//
2003-09-20 12:43:52 +00:00
// ---------------------------------------------------------------------------
2003-07-28 01:44:38 +00:00
//
// int32 UnpauseMusic(void)
//
// Re-starts the music from where it was stopped.
//
2003-09-20 12:43:52 +00:00
// ===========================================================================
int32 FN_prepare_music(int32 *params) {
return IR_CONT;
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +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
2003-09-20 12:43:52 +00:00
int32 FN_play_music(int32 *params) { // updated by James on 10apr97
// 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
// Zdebug("FN_play_music(%d)", params[0]);
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
looping_music_id = params[0];
} else {
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
looping_music_id = 0;
}
2003-07-28 01:44:38 +00:00
// add the appropriate file extension & play it
2003-09-20 12:43:52 +00:00
if (g_sword2->_gameId == GID_SWORD2_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", res_man.WhichCd());
if (f.open(filename))
f.close();
else
strcpy(filename, "music.clu");
}
2003-07-28 01:44:38 +00:00
rv = g_sound->StreamCompMusic(filename, params[0], loopFlag);
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
#ifdef _SWORD2_DEBUG
2003-07-28 01:44:38 +00:00
if (rv)
Zdebug("ERROR: StreamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv);
2003-09-20 12:43:52 +00:00
#endif
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
// Zdebug("FN_play_music(%d) returning", params[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
}
2003-09-20 12:43:52 +00:00
int32 FN_stop_music(int32 *params) { // called from script only
2003-07-28 01:44:38 +00:00
// params: none
2003-09-20 12:43:52 +00:00
looping_music_id = 0; // clear the 'looping' flag
g_sound->StopMusic();
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
void Kill_music(void) { // James22aug97
looping_music_id = 0; // clear the 'looping' flag
g_sound->StopMusic();
2003-07-28 01:44:38 +00:00
2003-09-20 12:43:52 +00:00
/* I don't think this is needed with our music code
2003-07-28 01:44:38 +00:00
// THIS BIT CAUSES THE MUSIC TO STOP INSTANTLY!
2003-09-20 12:43:52 +00:00
for(int count=0; count<16; count++)
g_sound->UpdateCompSampleStreaming();
2003-09-20 12:43:52 +00:00
*/
2003-07-28 01:44:38 +00:00
}
2003-09-20 12:43:52 +00:00
int32 FN_check_music_playing(int32 *params) { // James (30july97)
// 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
RESULT = g_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
void PauseAllSound(void) { // James25july97
uint32 rv;
2003-07-28 01:44:38 +00:00
rv = g_sound->PauseMusic();
2003-07-28 01:44:38 +00:00
if (rv != RD_OK)
Zdebug("ERROR: PauseMusic() returned %.8x in PauseAllSound()", rv);
rv = g_sound->PauseSpeech();
2003-07-28 01:44:38 +00:00
if (rv != RD_OK)
Zdebug("ERROR: PauseSpeech() returned %.8x in PauseAllSound()", rv);
rv = g_sound->PauseFx();
2003-07-28 01:44:38 +00:00
if (rv != RD_OK)
Zdebug("ERROR: PauseFx() returned %.8x in PauseAllSound()", rv);
}
2003-09-20 12:43:52 +00:00
void UnpauseAllSound(void) { // James25july97
uint32 rv;
2003-07-28 01:44:38 +00:00
rv = g_sound->UnpauseMusic();
2003-07-28 01:44:38 +00:00
if (rv != RD_OK)
Zdebug("ERROR: UnpauseMusic() returned %.8x in UnpauseAllSound()", rv);
rv = g_sound->UnpauseSpeech();
2003-07-28 01:44:38 +00:00
if (rv != RD_OK)
Zdebug("ERROR: UnpauseSpeech() returned %.8x in UnpauseAllSound()", rv);
rv = g_sound->UnpauseFx();
2003-07-28 01:44:38 +00:00
if (rv != RD_OK)
Zdebug("ERROR: UnpauseFx() returned %.8x in UnpauseAllSound()", rv);
}