aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sound
diff options
context:
space:
mode:
authorColin Snover2017-06-07 20:59:34 -0500
committerColin Snover2017-06-09 23:00:14 -0500
commit85e35943fe27b99a91d97eace3072117c2073f69 (patch)
treeb298ba66c3cdafde9d48f9d8b6690fb19cec3eef /engines/sci/sound
parent4311d1b18274d25384c5492c27f0611db28d6c4a (diff)
downloadscummvm-rg350-85e35943fe27b99a91d97eace3072117c2073f69.tar.gz
scummvm-rg350-85e35943fe27b99a91d97eace3072117c2073f69.tar.bz2
scummvm-rg350-85e35943fe27b99a91d97eace3072117c2073f69.zip
SCI32: Implement kLock & kDoAudio(1) for SCI32
1. Unlocking all resources of a type using a resource ID of -1 is gone in SCI32; 2. Audio locks need to be serialized starting in GK2 for the game's modified kDoAudio(1) call; 3. Audio locks in SCI3 must work more like SSCI, since at least Lighthouse's `BackMusic::fade` method will attempt to unlock audio that was never locked by a script. In SSCI (and now in ScummVM too) this is a no-op; previously in ScummVM, it would remove Audio32's own lock on the audio resource, resulting in a use-after-free; 4. kDoAudio(1) starting in GK2 returns the number of active *not-in-memory* channels being played, not the total number of active channels. Fixes Trac#9675.
Diffstat (limited to 'engines/sci/sound')
-rw-r--r--engines/sci/sound/audio32.cpp47
-rw-r--r--engines/sci/sound/audio32.h28
2 files changed, 70 insertions, 5 deletions
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index b61bbd9a4e..f47002bb59 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -354,6 +354,20 @@ int Audio32::readBuffer(Audio::st_sample_t *buffer, const int numSamples) {
#pragma mark -
#pragma mark Channel management
+uint8 Audio32::getNumUnlockedChannels() const {
+ Common::StackLock lock(_mutex);
+
+ uint8 numChannels = 0;
+ for (uint i = 0; i < _numActiveChannels; ++i) {
+ const AudioChannel &channel = getChannel(i);
+ if (!channel.robot && Common::find(_lockedResourceIds.begin(), _lockedResourceIds.end(), channel.id) == _lockedResourceIds.end()) {
+ ++numChannels;
+ }
+ }
+
+ return numChannels;
+}
+
int16 Audio32::findChannelByArgs(int argc, const reg_t *argv, const int startIndex, const reg_t soundNode) const {
// NOTE: argc/argv are already reduced by one in our engine because
// this call is always made from a subop, so no reduction for the
@@ -420,6 +434,21 @@ int16 Audio32::findChannelById(const ResourceId resourceId, const reg_t soundNod
return kNoExistingChannel;
}
+void Audio32::lockResource(const ResourceId resourceId, const bool lock) {
+ Common::StackLock slock(_mutex);
+
+ LockList::iterator it = Common::find(_lockedResourceIds.begin(), _lockedResourceIds.end(), resourceId);
+ if (it != _lockedResourceIds.end()) {
+ if (!lock) {
+ _lockedResourceIds.erase(it);
+ }
+ } else {
+ if (lock) {
+ _lockedResourceIds.push_back(resourceId);
+ }
+ }
+}
+
void Audio32::freeUnusedChannels() {
Common::StackLock lock(_mutex);
for (int channelIndex = 0; channelIndex < _numActiveChannels; ++channelIndex) {
@@ -1037,10 +1066,6 @@ bool Audio32::hasSignal() const {
reg_t Audio32::kernelPlay(const bool autoPlay, const int argc, const reg_t *const argv) {
Common::StackLock lock(_mutex);
- if (argc == 0) {
- return make_reg(0, _numActiveChannels);
- }
-
const int16 channelIndex = findChannelByArgs(argc, argv, 0, NULL_REG);
ResourceId resourceId;
bool loop;
@@ -1214,6 +1239,20 @@ void Audio32::printAudioList(Console *con) const {
channel.stopChannelOnFade ? ", stopping" : "");
}
}
+
+ if (g_sci->_features->hasSci3Audio()) {
+ con->debugPrintf("\nLocks: ");
+ if (_lockedResourceIds.size()) {
+ const char *separator = "";
+ for (LockList::const_iterator it = _lockedResourceIds.begin(); it != _lockedResourceIds.end(); ++it) {
+ con->debugPrintf("%s%s", separator, it->toString().c_str());
+ separator = ", ";
+ }
+ } else {
+ con->debugPrintf("none");
+ }
+ con->debugPrintf("\n");
+ }
}
} // End of namespace Sci
diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h
index dd48b39865..27f7e8f3a1 100644
--- a/engines/sci/sound/audio32.h
+++ b/engines/sci/sound/audio32.h
@@ -157,11 +157,13 @@ enum AudioChannelIndex {
* engine, since the system mixer does not support all the
* features of SCI.
*/
-class Audio32 : public Audio::AudioStream {
+class Audio32 : public Audio::AudioStream, public Common::Serializable {
public:
Audio32(ResourceManager *resMan);
~Audio32();
+ virtual void saveLoadWithSerializer(Common::Serializer &s);
+
enum {
/**
* The maximum channel volume.
@@ -205,6 +207,19 @@ public:
}
/**
+ * Gets the number of currently active channels that are playing from
+ * unlocked resources.
+ *
+ * @note In SSCI, this function would actually return the number of channels
+ * whose audio data were not loaded into memory. In practice, the signal for
+ * placing audio data into memory was a call to kLock, so since we do not
+ * follow how SSCI works when it comes to resource management, the lock
+ * state is used as an (apparently) successful proxy for this information
+ * instead.
+ */
+ uint8 getNumUnlockedChannels() const;
+
+ /**
* Finds a channel that is already configured for the
* given audio sample.
*
@@ -219,7 +234,13 @@ public:
*/
int16 findChannelById(const ResourceId resourceId, const reg_t soundNode = NULL_REG) const;
+ /**
+ * Sets or clears a lock on the given resource ID.
+ */
+ void lockResource(const ResourceId resourceId, const bool lock);
+
private:
+ typedef Common::Array<ResourceId> LockList;
typedef Common::Array<Resource *> UnlockList;
/**
@@ -252,6 +273,11 @@ private:
UnlockList _resourcesToUnlock;
/**
+ * The list of resource IDs that have been locked by game scripts.
+ */
+ LockList _lockedResourceIds;
+
+ /**
* Gets the audio channel at the given index.
*/
inline AudioChannel &getChannel(const int16 channelIndex) {