diff options
Diffstat (limited to 'sword2/sound.cpp')
-rw-r--r-- | sword2/sound.cpp | 245 |
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(); |