aboutsummaryrefslogtreecommitdiff
path: root/sword2/sound.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sword2/sound.cpp')
-rw-r--r--sword2/sound.cpp245
1 files changed, 101 insertions, 144 deletions
diff --git a/sword2/sound.cpp b/sword2/sound.cpp
index 7d8c4b157e..28c4218d78 100644
--- a/sword2/sound.cpp
+++ b/sword2/sound.cpp
@@ -38,94 +38,100 @@
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 fxq by clearing all the entries.
+ * 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.
*/
void Sword2Engine::initFxQueue(void) {
for (int i = 0; i < FXQ_LENGTH; i++)
- _fxQueue[i].resource = 0;
+ 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;
+ }
+ }
}
/**
- * Process the fx queue once every game cycle
+ * Process the FX queue once every game cycle
*/
void Sword2Engine::processFxQueue(void) {
for (int i = 0; i < FXQ_LENGTH; i++) {
- if (!_fxQueue[i].resource)
+ if (!fxQueue[i].resource)
continue;
- switch (_fxQueue[i].type) {
+ switch (fxQueue[i].type) {
case FX_RANDOM:
// 1 in 'delay' chance of this fx occurring
- if (_rnd.getRandomNumber(_fxQueue[i].delay) == 0)
+ if (_rnd.getRandomNumber(fxQueue[i].delay) == 0)
triggerFx(i);
break;
case FX_SPOT:
- if (_fxQueue[i].delay)
- _fxQueue[i].delay--;
+ if (fxQueue[i].delay)
+ fxQueue[i].delay--;
else {
triggerFx(i);
- _fxQueue[i].type = FX_SPOT2;
+ 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.
+ // Once the FX has finished remove it from the queue.
if (!_sound->isFxPlaying(i + 1)) {
- _fxQueue[i].resource = 0;
- _sound->closeFx(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;
}
}
}
-void Sword2Engine::triggerFx(uint8 j) {
- byte *data;
- int32 id;
- uint32 rv;
+void Sword2Engine::triggerFx(uint8 i) {
+ int type;
- id = (uint32) j + 1; // because 0 is not a valid id
-
- if (_fxQueue[j].type == FX_SPOT) {
- // load in the sample
- data = _resman->openResource(_fxQueue[j].resource);
- data += sizeof(StandardHeader);
- // wav data gets copied to sound memory
- rv = _sound->playFx(id, data, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXSPOT);
- // release the sample
- _resman->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 = _sound->playFx(id, NULL, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXSPOT);
- } else {
- // Looped
- rv = _sound->playFx(id, NULL, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXLOOP);
- }
- }
+ if (fxQueue[i].type == FX_LOOP)
+ type = RDSE_FXLOOP;
+ else
+ type = RDSE_FXSPOT;
+ uint32 rv = _sound->playFx(i + 1, fxQueue[i].data, fxQueue[i].volume, fxQueue[i].pan, type);
if (rv)
debug(5, "SFX ERROR: playFx() returned %.8x", rv);
}
-/**
- * Stop all looped & random fx and clear the entire queue
- */
-
-void Sword2Engine::clearFxQueue(void) {
- // stop all fx & remove the samples from sound memory
- _sound->clearAllFx();
-
- // clean out the queue
- initFxQueue();
-}
-
void Sword2Engine::killMusic(void) {
_loopingMusicId = 0; // clear the 'looping' flag
_sound->stopMusic();
@@ -159,15 +165,6 @@ int32 Logic::fnPlayFx(int32 *params) {
// .
// fnStopFx (fx_water);
- uint8 j = 0;
- byte *data;
- uint32 id;
- uint32 rv;
-
-#ifdef _SWORD2_DEBUG
- StandardHeader *header;
-#endif
-
if (_vm->_wantSfxDebug) {
char type[10];
@@ -191,76 +188,46 @@ int32 Logic::fnPlayFx(int32 *params) {
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);
}
- while (j < FXQ_LENGTH && _vm->_fxQueue[j].resource != 0)
- j++;
+ int i;
- if (j == FXQ_LENGTH)
- return IR_CONT;
+ // Find a free slot in the FX queue
- _vm->_fxQueue[j].resource = params[0]; // wav resource id
- _vm->_fxQueue[j].type = params[1]; // FX_SPOT, FX_LOOP or FX_RANDOM
-
- if (_vm->_fxQueue[j].type == FX_RANDOM) {
- // 'delay' param is the intended average no. seconds between
- // playing this effect
- _vm->_fxQueue[j].delay = params[2] * 12;
- } else {
- // FX_SPOT or FX_LOOP:
- // 'delay' is no. frames to wait before playing
- _vm->_fxQueue[j].delay = params[2];
+ for (i = 0; i < FXQ_LENGTH; i++) {
+ if (!fxQueue[i].resource)
+ break;
}
- _vm->_fxQueue[j].volume = params[3]; // 0..16
- _vm->_fxQueue[j].pan = params[4]; // -16..16
-
- if (_vm->_fxQueue[j].type == FX_SPOT) {
- // "pre-load" the sample; this gets it into memory
- data = _vm->_resman->openResource(_vm->_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
- _vm->_resman->closeResource(_vm->_fxQueue[j].resource);
- } else {
- // random & looped fx
-
- id = (uint32) j + 1; // because 0 is not a valid id
-
- // load in the sample
- data = _vm->_resman->openResource(_vm->_fxQueue[j].resource);
+ if (i == FXQ_LENGTH) {
+ warning("No free slot in FX queue");
+ return IR_CONT;
+ }
-#ifdef _SWORD2_DEBUG
- header = (StandardHeader *) data;
- if (header->fileType != WAV_FILE)
- error("fnPlayFx given invalid resource");
-#endif
+ fxQueue[i].resource = params[0];
+ fxQueue[i].type = params[1];
+ fxQueue[i].delay = params[2];
- data += sizeof(StandardHeader);
+ 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;
+ }
- // copy it to sound memory, using position in queue as 'id'
- rv = _vm->_sound->openFx(id, data);
+ fxQueue[i].volume = params[3];
+ fxQueue[i].pan = params[4];
- if (rv)
- debug(5, "SFX ERROR: openFx() returned %.8x", rv);
+ byte *data = _vm->_resman->openResource(params[0]);
+ StandardHeader *header = (StandardHeader *) data;
- // release the sample
- _vm->_resman->closeResource(_vm->_fxQueue[j].resource);
- }
+ assert(header->fileType == WAV_FILE);
- if (_vm->_fxQueue[j].type == FX_LOOP) {
- // play now, rather than in processFxQueue where it was
- // getting played again & again!
- _vm->triggerFx(j);
- }
+ fxQueue[i].data = data + sizeof(StandardHeader);
- // in case we want to call fnStopFx() later, to kill this fx
- // (mainly for FX_LOOP & FX_RANDOM)
+ // 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.
- _scriptVars[RESULT] = j;
+ _scriptVars[RESULT] = i;
return IR_CONT;
}
@@ -270,7 +237,7 @@ int32 Logic::fnSoundFetch(int32 *params) {
}
/**
- * Alter the volume and pan of a currently playing fx
+ * Alter the volume and pan of a currently playing FX
*/
int32 Logic::fnSetFxVolAndPan(int32 *params) {
@@ -281,14 +248,12 @@ int32 Logic::fnSetFxVolAndPan(int32 *params) {
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>
- _vm->_sound->setFxIdVolumePan(1 + params[0], params[1], params[2]);
+ _vm->_sound->setFxIdVolumePan(params[0] + 1, params[1], params[2]);
return IR_CONT;
}
/**
- * Alter the volume of a currently playing fx
+ * Alter the volume of a currently playing FX
*/
int32 Logic::fnSetFxVol(int32 *params) {
@@ -296,41 +261,33 @@ int32 Logic::fnSetFxVol(int32 *params) {
// fnPlayFx
// 1 new volume (0..16)
- // SetFxIdVolume(int32 id, uint8 vol);
- _vm->_sound->setFxIdVolume(1 + params[0], params[1]);
+ _vm->_sound->setFxIdVolume(params[0] + 1, params[1]);
return IR_CONT;
}
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 (_vm->_fxQueue[j].type == FX_RANDOM || _vm->_fxQueue[j].type == FX_LOOP) {
- id = (uint32) j + 1; // because 0 is not a valid id
+ int32 i = params[0];
+ uint32 rv = _vm->_sound->stopFx(i + 1);
- // stop fx & remove sample from sound memory
- rv = _vm->_sound->closeFx(id);
+ if (rv)
+ debug(5, "SFX ERROR: closeFx() returned %.8x", rv);
- if (rv)
- debug(5, "SFX ERROR: closeFx() returned %.8x", rv);
+ // Remove from queue
+ if (fxQueue[i].resource) {
+ _vm->_resman->closeResource(fxQueue[i].resource);
+ fxQueue[i].resource = 0;
}
- // remove from queue
- _vm->_fxQueue[j].resource = 0;
-
return IR_CONT;
}
-int32 Logic::fnStopAllFx(int32 *params) {
- // Stops all looped & random fx and clears the entire queue
+/**
+ * Stops all FX and clears the entire FX queue.
+ */
+int32 Logic::fnStopAllFx(int32 *params) {
// params: none
_vm->clearFxQueue();