aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--sword2/driver/animation.cpp8
-rw-r--r--sword2/driver/d_sound.cpp144
-rw-r--r--sword2/driver/d_sound.h7
-rw-r--r--sword2/resman.cpp11
-rw-r--r--sword2/sound.cpp245
-rw-r--r--sword2/sound.h6
-rw-r--r--sword2/sword2.h10
8 files changed, 173 insertions, 261 deletions
diff --git a/NEWS b/NEWS
index b269269ceb..ab2aabfdfc 100644
--- a/NEWS
+++ b/NEWS
@@ -23,7 +23,8 @@ For a more comprehensive changelog for the latest experimental CVS code, see:
- ??? [TODO: Somebody of the Sword1 team please fill this in]
Sword2:
- - Simplified the memory/resource management.
+ - Simplified the memory/resource management...
+ - ...which led to simplified sound effects handling
- Various minor bugfixes.
BASS
diff --git a/sword2/driver/animation.cpp b/sword2/driver/animation.cpp
index 53dad119f5..b7d177ed84 100644
--- a/sword2/driver/animation.cpp
+++ b/sword2/driver/animation.cpp
@@ -326,8 +326,8 @@ int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], byte *mus
// the animated cut-scenes, so this seems like a good place to close
// both of them.
- _vm->_sound->closeFx(-1);
- _vm->_sound->closeFx(-2);
+ _vm->_sound->stopFx(-1);
+ _vm->_sound->stopFx(-2);
return RD_OK;
#else
@@ -478,8 +478,8 @@ int32 MoviePlayer::playDummy(const char *filename, MovieTextObject *text[], byte
// the animated cut-scenes, so this seems like a good place to close
// both of them.
- _vm->_sound->closeFx(-1);
- _vm->_sound->closeFx(-2);
+ _vm->_sound->stopFx(-1);
+ _vm->_sound->stopFx(-2);
return RD_OK;
}
diff --git a/sword2/driver/d_sound.cpp b/sword2/driver/d_sound.cpp
index e6b547fdae..b5cf40d9c2 100644
--- a/sword2/driver/d_sound.cpp
+++ b/sword2/driver/d_sound.cpp
@@ -1029,77 +1029,13 @@ bool Sound::isFxPlaying(int32 id) {
}
/**
- * This function opens a sound effect ready for playing. A unique id should be
- * passed in so that each effect can be referenced individually.
- * @param id the unique sound id
- * @param data the WAV data
- * @warning Zero is not a valid id
- */
-
-int32 Sound::openFx(int32 id, byte *data) {
- if (!_soundOn)
- return RD_OK;
-
- if (id == 0)
- return RDERR_INVALIDID;
-
- if (getFxIndex(id) != MAXFX)
- return RDERR_FXALREADYOPEN;
-
- // Find a free slot
- int32 fxi = getFxIndex(0);
-
- if (fxi == MAXFX) {
- warning("openFx: Running out of sound slots");
-
- // There isn't any free sound handle available. This usually
- // shouldn't happen, but if it does we expire the first sound
- // effect that isn't currently playing.
-
- for (fxi = 0; fxi < MAXFX; fxi++)
- if (!_fx[fxi]._handle.isActive())
- break;
-
- // Still no dice? I give up!
-
- if (fxi == MAXFX) {
- warning("openFx: No free sound slots");
- return RDERR_NOFREEBUFFERS;
- }
- }
-
- _fx[fxi]._id = id;
- _fx[fxi]._flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_LITTLE_ENDIAN;
-
- WavInfo wavInfo;
-
- if (!getWavInfo(data, &wavInfo)) {
- warning("openFx: Not a valida WAV file");
- return RDERR_INVALIDWAV;
- }
-
- if (wavInfo.channels == 2)
- _fx[fxi]._flags |= SoundMixer::FLAG_STEREO;
-
- _fx[fxi]._rate = wavInfo.rate;
- _fx[fxi]._bufSize = wavInfo.samples;
-
- // Fill the speech buffer with data
- free(_fx[fxi]._buf);
- _fx[fxi]._buf = (uint16 *) malloc(_fx[fxi]._bufSize);
- memcpy(_fx[fxi]._buf, wavInfo.data, _fx[fxi]._bufSize);
-
- return RD_OK;
-}
-
-/**
* This function closes a sound effect which has been previously opened for
* playing. Sound effects must be closed when they are finished with, otherwise
* you will run out of sound effect buffers.
* @param id the id of the sound to close
*/
-int32 Sound::closeFx(int32 id) {
+int32 Sound::stopFx(int32 id) {
int i;
if (!_soundOn)
@@ -1133,43 +1069,69 @@ int32 Sound::playFx(int32 id, byte *data, uint8 vol, int8 pan, uint8 type) {
return RD_OK;
byte volume = _fxMuted ? 0 : vol * _fxVol;
- int8 p = _panTable[pan + 16];
- int32 i, hr;
- if (data) {
- // All lead-ins and lead-outs I've heard are music, so we use
- // the music volume setting for them.
+ // All lead-ins and lead-outs I've heard are music, so we use
+ // the music volume setting for them.
- if (type == RDSE_FXLEADIN || type == RDSE_FXLEADOUT) {
- id = (type == RDSE_FXLEADIN) ? -2 : -1;
- volume = _musicMuted ? 0 : _musicVolTable[_musicVol];
- }
+ if (type == RDSE_FXLEADIN || type == RDSE_FXLEADOUT) {
+ id = (type == RDSE_FXLEADIN) ? -2 : -1;
+ volume = _musicMuted ? 0 : _musicVolTable[_musicVol];
+ }
+
+ WavInfo wavInfo;
- hr = openFx(id, data);
- if (hr != RD_OK)
- return hr;
+ if (!getWavInfo(data, &wavInfo)) {
+ warning("playFx: Not a valid WAV file");
+ return RDERR_INVALIDWAV;
}
- i = getFxIndex(id);
+ int32 fxi = getFxIndex(id);
- if (i == MAXFX) {
- if (data) {
- warning("playFx(%d, %d, %d, %d) - Not found", id, vol, pan, type);
- return RDERR_FXFUCKED;
- } else {
- warning("playFx(%d, %d, %d, %d) - Not open", id, vol, pan, type);
- return RDERR_FXNOTOPEN;
+ if (fxi == MAXFX) {
+ // Find a free slot
+ fxi = getFxIndex(0);
+
+ if (fxi == MAXFX) {
+ warning("openFx: Running out of sound slots");
+
+ // There aren't any free sound handles available. This
+ // usually shouldn't happen, but if it does we expire
+ // the first sound effect that isn't currently playing.
+
+ for (fxi = 0; fxi < MAXFX; fxi++)
+ if (!_fx[fxi]._handle.isActive())
+ break;
+
+ // Still no dice? I give up!
+
+ if (fxi == MAXFX) {
+ warning("openFx: No free sound slots");
+ return RDERR_NOFREEBUFFERS;
+ }
}
+
+ _fx[fxi]._id = id;
}
+ if (_fx[fxi]._handle.isActive())
+ return RDERR_FXALREADYOPEN;
+
+ uint32 flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_LITTLE_ENDIAN;
+
+ if (wavInfo.channels == 2)
+ flags |= SoundMixer::FLAG_STEREO;
+
+
if (type == RDSE_FXLOOP)
- _fx[i]._flags |= SoundMixer::FLAG_LOOP;
+ flags |= SoundMixer::FLAG_LOOP;
else
- _fx[i]._flags &= ~SoundMixer::FLAG_LOOP;
+ flags &= ~SoundMixer::FLAG_LOOP;
- _fx[i]._volume = vol;
+ _fx[fxi]._volume = vol;
+
+ int8 p = _panTable[pan + 16];
- _vm->_mixer->playRaw(&_fx[i]._handle, _fx[i]._buf, _fx[i]._bufSize, _fx[i]._rate, _fx[i]._flags, -1, volume, p);
+ _vm->_mixer->playRaw(&_fx[fxi]._handle, wavInfo.data, wavInfo.samples, wavInfo.rate, flags, -1, volume, p);
return RD_OK;
}
@@ -1177,12 +1139,8 @@ int32 Sound::playFx(int32 id, byte *data, uint8 vol, int8 pan, uint8 type) {
void Sound::stopFxHandle(int i) {
if (_fx[i]._id) {
_vm->_mixer->stopHandle(_fx[i]._handle);
- free(_fx[i]._buf);
_fx[i]._id = 0;
_fx[i]._paused = false;
- _fx[i]._flags = 0;
- _fx[i]._bufSize = 0;
- _fx[i]._buf = NULL;
}
}
diff --git a/sword2/driver/d_sound.h b/sword2/driver/d_sound.h
index 19a3458510..ef10dd79e4 100644
--- a/sword2/driver/d_sound.h
+++ b/sword2/driver/d_sound.h
@@ -44,10 +44,6 @@ struct FxHandle {
int32 _id;
bool _paused;
int8 _volume;
- uint16 _rate;
- uint32 _flags;
- uint16 *_buf;
- int32 _bufSize;
PlayingSoundHandle _handle;
};
@@ -157,9 +153,8 @@ public:
void pauseFxForSequence(void);
void unpauseFx(void);
bool isFxPlaying(int32 id);
- int32 openFx(int32 id, uint8 *data);
- int32 closeFx(int32 id);
int32 playFx(int32 id, uint8 *data, uint8 vol, int8 pan, uint8 type);
+ int32 stopFx(int32 id);
void clearAllFx(void);
};
diff --git a/sword2/resman.cpp b/sword2/resman.cpp
index 245f9819dd..c9c424b482 100644
--- a/sword2/resman.cpp
+++ b/sword2/resman.cpp
@@ -40,8 +40,9 @@ namespace Sword2 {
// is located in and the number within the cluster
// If 0, resouces are expelled immediately when they are closed. At the moment
-// this causes the game to crash, which seems like a bug to me. In fact, it
-// could be a clue to the mysterious and infrequent crashes...
+// this causes the sound queue to run out of slots. My only theory is that it's
+// a script that gets reloaded over and over. That'd clear its local variables
+// which I guess may cause it to set up the sounds over and over.
#define CACHE_CLUSTERS 1
@@ -768,6 +769,12 @@ void ResourceManager::removeAll(void) {
void ResourceManager::killAll(bool wantInfo) {
int nuked = 0;
+ // We need to clear the FX queue, because otherwise the sound system
+ // will still believe that the sound resources are in memory, and that
+ // it's ok to close them.
+
+ _vm->clearFxQueue();
+
for (uint i = 0; i < _totalResFiles; i++) {
// Don't nuke the global variables or the player object!
if (i == 1 || i == CUR_PLAYER_ID)
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();
diff --git a/sword2/sound.h b/sword2/sound.h
index 13ece67851..6c7baabe24 100644
--- a/sword2/sound.h
+++ b/sword2/sound.h
@@ -38,10 +38,14 @@ namespace Sword2 {
// fx types
enum {
+ // These three types correspond to types set by the scripts
FX_SPOT = 0,
FX_LOOP = 1,
FX_RANDOM = 2,
- FX_SPOT2 = 3
+
+ // These are used for FX queue bookkeeping
+ FX_SPOT2 = 3,
+ FX_LOOPING = 4
};
} // End of namespace Sword2
diff --git a/sword2/sword2.h b/sword2/sword2.h
index 2d621622e7..588dd72106 100644
--- a/sword2/sword2.h
+++ b/sword2/sword2.h
@@ -321,16 +321,6 @@ public:
void setScrolling(void);
- struct FxQueueEntry {
- uint32 resource; // resource id of sample
- 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
- };
-
- FxQueueEntry _fxQueue[FXQ_LENGTH];
-
// used to store id of tunes that loop, for save & restore
uint32 _loopingMusicId;