[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