[Scummvm-git-logs] scummvm master -> 486ab6c58b2e673f24af3feeaa4b16399444f7a9

elasota noreply at scummvm.org
Mon Jul 18 00:03:53 UTC 2022


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
486ab6c58b MTROPOLIS: Add sound effect modifier support


Commit: 486ab6c58b2e673f24af3feeaa4b16399444f7a9
    https://github.com/scummvm/scummvm/commit/486ab6c58b2e673f24af3feeaa4b16399444f7a9
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-17T20:02:57-04:00

Commit Message:
MTROPOLIS: Add sound effect modifier support

Changed paths:
  A engines/mtropolis/audio_player.cpp
  A engines/mtropolis/audio_player.h
    engines/mtropolis/elements.cpp
    engines/mtropolis/modifiers.cpp
    engines/mtropolis/modifiers.h
    engines/mtropolis/module.mk


diff --git a/engines/mtropolis/audio_player.cpp b/engines/mtropolis/audio_player.cpp
new file mode 100644
index 00000000000..5fb33d3eff4
--- /dev/null
+++ b/engines/mtropolis/audio_player.cpp
@@ -0,0 +1,119 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mtropolis/audio_player.h"
+#include "mtropolis/assets.h"
+
+namespace MTropolis {
+
+AudioPlayer::AudioPlayer(Audio::Mixer *mixer, byte volume, int8 balance, const Common::SharedPtr<AudioMetadata> &metadata, const Common::SharedPtr<CachedAudio> &audio, bool isLooping, size_t currentPos, size_t startPos, size_t endPos)
+	: _metadata(metadata), _audio(audio), _isLooping(isLooping), _currentPos(currentPos), _startPos(startPos), _endPos(endPos), _exhausted(false), _mixer(nullptr) {
+	if (_startPos >= _endPos) {
+		// ???
+		_exhausted = true;
+		_isLooping = false;
+	}
+	if (_currentPos < _startPos)
+		_currentPos = _startPos;
+
+	if (!_exhausted) {
+		_mixer = mixer;
+		mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
+	}
+}
+
+AudioPlayer::~AudioPlayer() {
+	stop();
+}
+
+int AudioPlayer::readBuffer(int16 *buffer, const int numSamplesTimesChannelCount) {
+	Common::StackLock lock(_mutex);
+
+	int samplesRead = 0;
+	if (_exhausted)
+		return 0;
+
+	uint8 numChannels = _metadata->channels;
+
+	size_t numSamples = numSamplesTimesChannelCount / numChannels;
+
+	while (numSamples > 0) {
+		size_t samplesAvailable = _endPos - _currentPos;
+		if (samplesAvailable == 0) {
+			if (_isLooping) {
+				_currentPos = _startPos;
+				continue;
+			} else {
+				_exhausted = true;
+				break;
+			}
+		}
+
+		size_t numSamplesThisIteration = numSamples;
+		if (numSamplesThisIteration > samplesAvailable)
+			numSamplesThisIteration = samplesAvailable;
+
+		size_t numSampleValues = numSamplesThisIteration * numChannels;
+		// TODO: Support more formats
+		if (_metadata->bitsPerSample == 8 && _metadata->encoding == AudioMetadata::kEncodingUncompressed) {
+			const uint8 *inSamples = static_cast<const uint8 *>(_audio->getData()) + _currentPos * numChannels;
+			for (size_t i = 0; i < numSampleValues; i++)
+				buffer[i] = (inSamples[i] - 0x80) * 256;
+		} else if (_metadata->bitsPerSample == 16 && _metadata->encoding == AudioMetadata::kEncodingUncompressed) {
+			const int16 *inSamples = static_cast<const int16 *>(_audio->getData()) + _currentPos * numChannels;
+			memcpy(buffer, inSamples, sizeof(int16) * numSampleValues);
+		}
+
+		buffer += numSampleValues;
+		numSamples -= numSamplesThisIteration;
+
+		samplesRead += numSamplesThisIteration * numChannels;
+		_currentPos += numSamplesThisIteration;
+	}
+
+	return samplesRead;
+}
+
+bool AudioPlayer::isStereo() const {
+	return _metadata->channels == 2;
+}
+
+int AudioPlayer::getRate() const {
+	return _metadata->sampleRate;
+}
+
+bool AudioPlayer::endOfData() const {
+	return _exhausted;
+}
+
+void AudioPlayer::sendToMixer(Audio::Mixer *mixer, byte volume, int8 balance) {
+	mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
+}
+
+void AudioPlayer::stop() {
+	if (_mixer)
+		_mixer->stopHandle(_handle);
+
+	_exhausted = true;
+	_mixer = nullptr;
+}
+
+} // End of namespace MTropolis
diff --git a/engines/mtropolis/audio_player.h b/engines/mtropolis/audio_player.h
new file mode 100644
index 00000000000..4b627dadfb0
--- /dev/null
+++ b/engines/mtropolis/audio_player.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MTROPOLIS_AUDIO_PLAYER_H
+#define MTROPOLIS_AUDIO_PLAYER_H
+
+#include "common/mutex.h"
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
+namespace MTropolis {
+
+struct AudioMetadata;
+class CachedAudio;
+
+// Audio player, this does not support requeueing.  If the sound exhausts, then you must create a
+// new audio player.  In particular, since time is being tracked separately, if the loop status
+// changes when the timer thinks the sound should still be playing, but the sound has actually
+// exhausted, then the sound needs to be requeued.
+class AudioPlayer : public Audio::AudioStream {
+public:
+	AudioPlayer(Audio::Mixer *mixer, byte volume, int8 balance, const Common::SharedPtr<AudioMetadata> &metadata, const Common::SharedPtr<CachedAudio> &audio, bool isLooping, size_t currentPos, size_t startPos, size_t endPos);
+	~AudioPlayer();
+
+	int readBuffer(int16 *buffer, const int numSamples) override;
+	bool isStereo() const override;
+	int getRate() const override;
+	bool endOfData() const override;
+
+	void sendToMixer(Audio::Mixer *mixer, byte volume, int8 balance);
+	void stop();
+
+private:
+	Common::Mutex _mutex;
+
+	Common::SharedPtr<AudioMetadata> _metadata;
+	Common::SharedPtr<CachedAudio> _audio;
+	Audio::SoundHandle _handle;
+	bool _isLooping;
+	bool _exhausted;
+	size_t _currentPos;
+	size_t _startPos;
+	size_t _endPos;
+	Audio::Mixer *_mixer;
+};
+
+} // End of namespace MTropolis
+
+#endif
diff --git a/engines/mtropolis/elements.cpp b/engines/mtropolis/elements.cpp
index 772cd30ec3f..d9cb487af99 100644
--- a/engines/mtropolis/elements.cpp
+++ b/engines/mtropolis/elements.cpp
@@ -29,138 +29,15 @@
 #include "graphics/font.h"
 #include "graphics/managed_surface.h"
 
-#include "mtropolis/elements.h"
 #include "mtropolis/assets.h"
+#include "mtropolis/audio_player.h"
+#include "mtropolis/elements.h"
 #include "mtropolis/element_factory.h"
 #include "mtropolis/miniscript.h"
 #include "mtropolis/render.h"
 
 namespace MTropolis {
 
-
-// Audio player, this does not support requeueing.  If the sound exhausts, then you must create a
-// new audio player.  In particular, since time is being tracked separately, if the loop status
-// changes when the timer thinks the sound should still be playing, but the sound has actually
-// exhausted, then the sound needs to be requeued.
-class AudioPlayer : public Audio::AudioStream {
-public:
-	AudioPlayer(Audio::Mixer *mixer, byte volume, int8 balance, const Common::SharedPtr<AudioMetadata> &metadata, const Common::SharedPtr<CachedAudio> &audio, bool isLooping, size_t currentPos, size_t startPos, size_t endPos);
-	~AudioPlayer();
-	
-	int readBuffer(int16 *buffer, const int numSamples) override;
-	bool isStereo() const override;
-	int getRate() const override;
-	bool endOfData() const override;
-
-	void sendToMixer(Audio::Mixer *mixer, byte volume, int8 balance);
-	void stop();
-
-private:
-	Common::Mutex _mutex;
-
-	Common::SharedPtr<AudioMetadata> _metadata;
-	Common::SharedPtr<CachedAudio> _audio;
-	Audio::SoundHandle _handle;
-	bool _isLooping;
-	bool _exhausted;
-	size_t _currentPos;
-	size_t _startPos;
-	size_t _endPos;
-	Audio::Mixer *_mixer;
-};
-
-AudioPlayer::AudioPlayer(Audio::Mixer *mixer, byte volume, int8 balance, const Common::SharedPtr<AudioMetadata> &metadata, const Common::SharedPtr<CachedAudio> &audio, bool isLooping, size_t currentPos, size_t startPos, size_t endPos)
-	: _metadata(metadata), _audio(audio), _isLooping(isLooping), _currentPos(currentPos), _startPos(startPos), _endPos(endPos), _exhausted(false), _mixer(nullptr) {
-	if (_startPos >= _endPos) {
-		// ???
-		_exhausted = true;
-		_isLooping = false;
-	}
-	if (_currentPos < _startPos)
-		_currentPos = _startPos;
-
-	if (!_exhausted) {
-		_mixer = mixer;
-		mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
-	}
-}
-
-AudioPlayer::~AudioPlayer() {
-	stop();
-}
-
-int AudioPlayer::readBuffer(int16 *buffer, const int numSamplesTimesChannelCount) {
-	Common::StackLock lock(_mutex);
-
-	int samplesRead = 0;
-	if (_exhausted)
-		return 0;
-
-	uint8 numChannels = _metadata->channels;
-
-	size_t numSamples = numSamplesTimesChannelCount / numChannels;
-
-	while (numSamples > 0) {
-		size_t samplesAvailable = _endPos - _currentPos;
-		if (samplesAvailable == 0) {
-			if (_isLooping) {
-				_currentPos = _startPos;
-				continue;
-			} else {
-				_exhausted = true;
-				break;
-			}
-		}
-
-		size_t numSamplesThisIteration = numSamples;
-		if (numSamplesThisIteration > samplesAvailable)
-			numSamplesThisIteration = samplesAvailable;
-
-		size_t numSampleValues = numSamplesThisIteration * numChannels;
-		// TODO: Support more formats
-		if (_metadata->bitsPerSample == 8 && _metadata->encoding == AudioMetadata::kEncodingUncompressed) {
-			const uint8 *inSamples = static_cast<const uint8 *>(_audio->getData()) + _currentPos * numChannels;
-			for (size_t i = 0; i < numSampleValues; i++)
-				buffer[i] = (inSamples[i] - 0x80) * 256;
-		} else if (_metadata->bitsPerSample == 16 && _metadata->encoding == AudioMetadata::kEncodingUncompressed) {
-			const int16 *inSamples = static_cast<const int16 *>(_audio->getData()) + _currentPos * numChannels;
-			memcpy(buffer, inSamples, sizeof(int16) * numSampleValues);
-		}
-
-		buffer += numSampleValues;
-		numSamples -= numSamplesThisIteration;
-
-		samplesRead += numSamplesThisIteration * numChannels;
-		_currentPos += numSamplesThisIteration;
-	}
-
-	return samplesRead;
-}
-
-bool AudioPlayer::isStereo() const {
-	return _metadata->channels == 2;
-}
-
-int AudioPlayer::getRate() const {
-	return _metadata->sampleRate;
-}
-
-bool AudioPlayer::endOfData() const {
-	return _exhausted;
-}
-
-void AudioPlayer::sendToMixer(Audio::Mixer *mixer, byte volume, int8 balance) {
-	mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
-}
-
-void AudioPlayer::stop() {
-	if (_mixer)
-		_mixer->stopHandle(_handle);
-
-	_exhausted = true;
-	_mixer = nullptr;
-}
-
 GraphicElement::GraphicElement() : _cacheBitmap(false) {
 }
 
diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index 6f93f8b12a1..845ffe04ef6 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -21,6 +21,8 @@
 
 #include "common/memstream.h"
 
+#include "mtropolis/assets.h"
+#include "mtropolis/audio_player.h"
 #include "mtropolis/miniscript.h"
 #include "mtropolis/modifiers.h"
 #include "mtropolis/modifier_factory.h"
@@ -532,7 +534,7 @@ bool SoundEffectModifier::load(ModifierLoaderContext &context, const Data::Sound
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
 
-	if (!_executeWhen.load(data.executeWhen) || !_terminateWhen.load(data.executeWhen))
+	if (!_executeWhen.load(data.executeWhen) || !_terminateWhen.load(data.terminateWhen))
 		return false;
 
 	if (data.assetID == Data::SoundEffectModifier::kSpecialAssetIDSystemBeep) {
@@ -546,6 +548,57 @@ bool SoundEffectModifier::load(ModifierLoaderContext &context, const Data::Sound
 	return true;
 }
 
+bool SoundEffectModifier::respondsToEvent(const Event &evt) const {
+	return _executeWhen.respondsTo(evt) || _terminateWhen.respondsTo(evt);
+}
+
+VThreadState SoundEffectModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
+	if (_terminateWhen.respondsTo(msg->getEvent())) {
+		if (_player) {
+			_player->stop();
+			_player.reset();
+		}
+	} else if (_executeWhen.respondsTo(msg->getEvent())) {
+		if (_soundType == kSoundTypeAudioAsset) {
+			if (!_cachedAudio)
+				loadAndCacheAudio(runtime);
+
+			if (_cachedAudio) {
+				if (_player) {
+					_player->stop();
+					_player.reset();
+				}
+
+				size_t numSamples = _cachedAudio->getNumSamples(*_metadata);
+				_player.reset(new AudioPlayer(runtime->getAudioMixer(), 255, 0, _metadata, _cachedAudio, false, 0, 0, numSamples));
+			}
+		}
+	}
+
+	return kVThreadReturn;
+}
+
+void SoundEffectModifier::loadAndCacheAudio(Runtime *runtime) {
+	if (_cachedAudio)
+		return;
+
+	Project *project = runtime->getProject();
+	Common::SharedPtr<Asset> asset = project->getAssetByID(_assetID).lock();
+
+	if (!asset) {
+		warning("Sound effect modifier references asset %i but the asset isn't loaded!", _assetID);
+		return;
+	}
+
+	if (asset->getAssetType() != kAssetTypeAudio) {
+		warning("Sound element assigned an asset that isn't audio");
+		return;
+	}
+
+	_cachedAudio = static_cast<AudioAsset *>(asset.get())->loadAndCacheAudio(runtime);
+	_metadata = static_cast<AudioAsset *>(asset.get())->getMetadata();
+}
+
 Common::SharedPtr<Modifier> SoundEffectModifier::shallowClone() const {
 	return Common::SharedPtr<Modifier>(new SoundEffectModifier(*this));
 }
diff --git a/engines/mtropolis/modifiers.h b/engines/mtropolis/modifiers.h
index 72be5f140c1..e6c14906870 100644
--- a/engines/mtropolis/modifiers.h
+++ b/engines/mtropolis/modifiers.h
@@ -30,6 +30,9 @@
 
 namespace MTropolis {
 
+struct AudioMetadata;
+class AudioPlayer;
+class CachedAudio;
 struct ModifierLoaderContext;
 class MiniscriptProgram;
 class MiniscriptReferences;
@@ -228,6 +231,9 @@ class SoundEffectModifier : public Modifier {
 public:
 	bool load(ModifierLoaderContext &context, const Data::SoundEffectModifier &data);
 
+	bool respondsToEvent(const Event &evt) const override;
+	VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
+
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Sound Effect Modifier"; }
 #endif
@@ -241,11 +247,17 @@ private:
 		kSoundTypeAudioAsset,
 	};
 
+	void loadAndCacheAudio(Runtime *runtime);
+
 	Event _executeWhen;
 	Event _terminateWhen;
 
 	SoundType _soundType;
 	uint32 _assetID;
+
+	Common::SharedPtr<CachedAudio> _cachedAudio;
+	Common::SharedPtr<AudioMetadata> _metadata;
+	Common::SharedPtr<AudioPlayer> _player;
 };
 
 class PathMotionModifierV2 : public Modifier {
diff --git a/engines/mtropolis/module.mk b/engines/mtropolis/module.mk
index d5a34f5e8dc..c8f0a9ce5dc 100644
--- a/engines/mtropolis/module.mk
+++ b/engines/mtropolis/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/mtropolis
 MODULE_OBJS = \
 	asset_factory.o \
 	assets.o \
+	audio_player.o \
 	boot.o \
 	core.o \
 	data.o \




More information about the Scummvm-git-logs mailing list