aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk/riven_sound.cpp
diff options
context:
space:
mode:
authorBastien Bouclet2016-08-08 07:35:55 +0200
committerBastien Bouclet2016-08-11 19:53:20 +0200
commit2756d6226b1567dbe650284c56d93d722af906b2 (patch)
treec08237d5dd570950dff2e087d6f592183570848a /engines/mohawk/riven_sound.cpp
parentaeee4888beb8ba15a32a438e4ac82c53d250e039 (diff)
downloadscummvm-rg350-2756d6226b1567dbe650284c56d93d722af906b2.tar.gz
scummvm-rg350-2756d6226b1567dbe650284c56d93d722af906b2.tar.bz2
scummvm-rg350-2756d6226b1567dbe650284c56d93d722af906b2.zip
MOHAWK: Add a Riven specific sound manager
- Add ambient sound fading - Fix ambient sound volume to use the list-level volume
Diffstat (limited to 'engines/mohawk/riven_sound.cpp')
-rw-r--r--engines/mohawk/riven_sound.cpp459
1 files changed, 459 insertions, 0 deletions
diff --git a/engines/mohawk/riven_sound.cpp b/engines/mohawk/riven_sound.cpp
new file mode 100644
index 0000000000..dd45f94ad3
--- /dev/null
+++ b/engines/mohawk/riven_sound.cpp
@@ -0,0 +1,459 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/system.h"
+
+#include "audio/audiostream.h"
+
+#include "mohawk/riven_sound.h"
+#include "mohawk/sound.h"
+
+namespace Mohawk {
+
+RivenSoundManager::RivenSoundManager(MohawkEngine *vm) :
+ _vm(vm),
+ _effect(nullptr),
+ _mainAmbientSoundId(-1),
+ _effectPlayOnDraw(false),
+ _nextFadeUpdate(0) {
+
+}
+
+RivenSoundManager::~RivenSoundManager() {
+ stopSound();
+ stopAllSLST(false);
+}
+
+Audio::RewindableAudioStream *RivenSoundManager::makeAudioStream(uint16 id) {
+ return makeMohawkWaveStream(_vm->getResource(ID_TWAV, id));
+}
+
+void RivenSoundManager::playSound(uint16 id, uint16 volume, bool playOnDraw) {
+ debug (0, "Playing sound %d", id);
+
+ stopSound();
+
+ Audio::RewindableAudioStream *rewindStream = makeAudioStream(id);
+ if (!rewindStream) {
+ warning("Unable to play sound with id %d", id);
+ return;
+ }
+
+ _effect = new RivenSound(_vm, rewindStream);
+ _effect->setVolume(volume);
+
+ _effectPlayOnDraw = playOnDraw;
+ if (!playOnDraw) {
+ _effect->play();
+ }
+}
+
+void RivenSoundManager::playSLST(uint16 index, uint16 card) {
+ Common::SeekableReadStream *slstStream = _vm->getResource(ID_SLST, card);
+
+ uint16 recordCount = slstStream->readUint16BE();
+
+ for (uint16 i = 0; i < recordCount; i++) {
+ SLSTRecord slstRecord;
+ slstRecord.index = slstStream->readUint16BE();
+
+ uint16 soundCount = slstStream->readUint16BE();
+ slstRecord.soundIds.resize(soundCount);
+
+ for (uint16 j = 0; j < soundCount; j++)
+ slstRecord.soundIds[j] = slstStream->readUint16BE();
+
+ slstRecord.fadeFlags = slstStream->readUint16BE();
+ slstRecord.loop = slstStream->readUint16BE();
+ slstRecord.globalVolume = slstStream->readUint16BE();
+ slstRecord.u0 = slstStream->readUint16BE(); // Unknown
+
+ if (slstRecord.u0 > 1)
+ warning("slstRecord.u0: %d non-boolean", slstRecord.u0);
+
+ slstRecord.suspend = slstStream->readUint16BE();
+
+ if (slstRecord.suspend != 0)
+ warning("slstRecord.u1: %d non-zero", slstRecord.suspend);
+
+ slstRecord.volumes.resize(soundCount);
+ slstRecord.balances.resize(soundCount);
+ slstRecord.u2.resize(soundCount);
+
+ for (uint16 j = 0; j < soundCount; j++)
+ slstRecord.volumes[j] = slstStream->readUint16BE();
+
+ for (uint16 j = 0; j < soundCount; j++)
+ slstRecord.balances[j] = slstStream->readSint16BE(); // negative = left, 0 = center, positive = right
+
+ for (uint16 j = 0; j < soundCount; j++) {
+ slstRecord.u2[j] = slstStream->readUint16BE(); // Unknown
+
+ if (slstRecord.u2[j] != 255 && slstRecord.u2[j] != 256)
+ warning("slstRecord.u2[%d]: %d not 255 or 256", j, slstRecord.u2[j]);
+ }
+
+ if (slstRecord.index == index) {
+ playSLST(slstRecord);
+ delete slstStream;
+ return;
+ }
+ }
+
+ delete slstStream;
+
+ // If we have no matching entries, we do nothing and just let
+ // the previous ambient sounds continue.
+}
+
+void RivenSoundManager::playSLST(const SLSTRecord &slstRecord) {
+ if (slstRecord.soundIds.empty()) {
+ return;
+ }
+
+ if (slstRecord.soundIds[0] == _mainAmbientSoundId) {
+ if (slstRecord.soundIds.size() > _ambientSounds.sounds.size()) {
+ addAmbientSounds(slstRecord);
+ }
+ setAmbientLooping(slstRecord.loop);
+ setTargetVolumes(slstRecord);
+ _ambientSounds.suspend = slstRecord.suspend;
+ if (slstRecord.suspend) {
+ freePreviousAmbientSounds();
+ pauseAmbientSounds();
+ applyTargetVolumes();
+ } else {
+ playAmbientSounds();
+ }
+ } else {
+ _mainAmbientSoundId = slstRecord.soundIds[0];
+ freePreviousAmbientSounds();
+ moveAmbientSoundsToPreviousSounds();
+ addAmbientSounds(slstRecord);
+ setAmbientLooping(slstRecord.loop);
+ setTargetVolumes(slstRecord);
+ _ambientSounds.suspend = slstRecord.suspend;
+ if (slstRecord.suspend) {
+ freePreviousAmbientSounds();
+ applyTargetVolumes();
+ } else {
+ startFadingAmbientSounds(slstRecord.fadeFlags);
+ }
+ }
+}
+
+void RivenSoundManager::stopAllSLST(bool fade) {
+ _mainAmbientSoundId = -1;
+ freePreviousAmbientSounds();
+ moveAmbientSoundsToPreviousSounds();
+ startFadingAmbientSounds(fade ? kFadeOutPreviousSounds : 0);
+}
+
+void RivenSoundManager::stopSound() {
+ if (_effect) {
+ delete _effect;
+ }
+ _effect = nullptr;
+ _effectPlayOnDraw = false;
+}
+
+void RivenSoundManager::addAmbientSounds(const SLSTRecord &record) {
+ if (record.soundIds.size() > _ambientSounds.sounds.size()) {
+ uint oldSize = _ambientSounds.sounds.size();
+
+ // Resize the list to the new size
+ _ambientSounds.sounds.resize(record.soundIds.size());
+
+ // Add new elements to the list
+ for (uint i = oldSize; i < _ambientSounds.sounds.size(); i++) {
+ Audio::RewindableAudioStream *stream = makeAudioStream(record.soundIds[i]);
+
+ RivenSound *sound = new RivenSound(_vm, stream);
+ sound->setVolume(record.volumes[i]);
+ sound->setBalance(record.balances[i]);
+
+ _ambientSounds.sounds[i].sound = sound;
+ _ambientSounds.sounds[i].targetVolume = record.volumes[i];
+ _ambientSounds.sounds[i].targetBalance = record.balances[i];
+ }
+ }
+}
+
+void RivenSoundManager::setTargetVolumes(const SLSTRecord &record) {
+ for (uint i = 0; i < _ambientSounds.sounds.size(); i++) {
+ _ambientSounds.sounds[i].targetVolume = record.volumes[i] * record.globalVolume / 256;
+ _ambientSounds.sounds[i].targetBalance = record.balances[i];
+ }
+ _ambientSounds.fading = true;
+}
+
+void RivenSoundManager::freePreviousAmbientSounds() {
+ for (uint i = 0; i < _previousAmbientSounds.sounds.size(); i++) {
+ delete _previousAmbientSounds.sounds[i].sound;
+ }
+ _previousAmbientSounds = AmbientSoundList();
+}
+
+void RivenSoundManager::moveAmbientSoundsToPreviousSounds() {
+ _previousAmbientSounds = _ambientSounds;
+ _ambientSounds = AmbientSoundList();
+}
+
+void RivenSoundManager::applyTargetVolumes() {
+ for (uint i = 0; i < _ambientSounds.sounds.size(); i++) {
+ AmbientSound &ambientSound = _ambientSounds.sounds[i];
+ RivenSound *sound = ambientSound.sound;
+ sound->setVolume(ambientSound.targetVolume);
+ sound->setBalance(ambientSound.targetBalance);
+ }
+ _ambientSounds.fading = false;
+}
+
+void RivenSoundManager::startFadingAmbientSounds(uint16 flags) {
+ for (uint i = 0; i < _ambientSounds.sounds.size(); i++) {
+ AmbientSound &ambientSound = _ambientSounds.sounds[i];
+ uint16 volume;
+ if (flags & kFadeInNewSounds) {
+ volume = 0;
+ } else {
+ volume = ambientSound.targetVolume;
+ }
+ ambientSound.sound->setVolume(volume);
+ }
+ _ambientSounds.fading = true;
+ playAmbientSounds();
+
+ if (!_previousAmbientSounds.sounds.empty()) {
+ if (flags) {
+ _previousAmbientSounds.fading = true;
+ } else {
+ freePreviousAmbientSounds();
+ }
+
+ for (uint i = 0; i < _previousAmbientSounds.sounds.size(); i++) {
+ AmbientSound &ambientSound = _previousAmbientSounds.sounds[i];
+ if (flags & kFadeOutPreviousSounds) {
+ ambientSound.targetVolume = 0;
+ } else {
+ ambientSound.sound->setVolume(ambientSound.targetVolume);
+ }
+ }
+ }
+}
+
+void RivenSoundManager::playAmbientSounds() {
+ for (uint i = 0; i < _ambientSounds.sounds.size(); i++) {
+ _ambientSounds.sounds[i].sound->play();
+ }
+}
+
+void RivenSoundManager::setAmbientLooping(bool loop) {
+ for (uint i = 0; i < _ambientSounds.sounds.size(); i++) {
+ _ambientSounds.sounds[i].sound->setLooping(loop);
+ }
+}
+
+void RivenSoundManager::triggerDrawSound() {
+ if (_effectPlayOnDraw && _effect) {
+ _effect->play();
+ }
+ _effectPlayOnDraw = false;
+}
+
+void RivenSoundManager::pauseAmbientSounds() {
+ for (uint i = 0; i < _ambientSounds.sounds.size(); i++) {
+ _ambientSounds.sounds[i].sound->pause();
+ }
+}
+
+void RivenSoundManager::updateSLST() {
+ uint32 time = _vm->_system->getMillis();
+ int32 delta = CLIP<int32>(time - _nextFadeUpdate, -50, 50);
+ if (_nextFadeUpdate == 0 || delta > 0) {
+ _nextFadeUpdate = time + 50 - delta;
+
+ if (_ambientSounds.fading) {
+ fadeAmbientSoundList(_ambientSounds);
+ }
+
+ if (_previousAmbientSounds.fading) {
+ fadeAmbientSoundList(_previousAmbientSounds);
+ }
+
+ if (!_previousAmbientSounds.sounds.empty() && !_ambientSounds.fading && !_previousAmbientSounds.fading) {
+ freePreviousAmbientSounds();
+ }
+ }
+}
+
+void RivenSoundManager::fadeAmbientSoundList(AmbientSoundList &list) {
+ list.fading = false;
+
+ for (uint i = 0; i < list.sounds.size(); i++) {
+ AmbientSound &ambientSound = list.sounds[i];
+ list.fading |= fadeVolume(ambientSound);
+ list.fading |= fadeBalance(ambientSound);
+ }
+}
+
+bool RivenSoundManager::fadeVolume(AmbientSound &ambientSound) {
+ uint16 volume = ambientSound.sound->getVolume();
+ float delta = (ambientSound.targetVolume - volume) / 30.0f;
+
+ if (ABS<float>(delta) < 0.01f) {
+ ambientSound.sound->setVolume(ambientSound.targetVolume);
+ return false;
+ } else {
+ // Make sure the increment is not zero once converted to an integer
+ if (delta > 0 && delta < 1) {
+ delta = 1;
+ } else if (delta < 0 && delta > -1) {
+ delta = -1;
+ }
+
+ ambientSound.sound->setVolume(volume + delta);
+ return true;
+ }
+}
+
+bool RivenSoundManager::fadeBalance(RivenSoundManager::AmbientSound &ambientSound) {
+ int16 balance = ambientSound.sound->getBalance();
+ float delta = (ambientSound.targetBalance - balance) / 10.0f;
+
+ if (ABS<float>(delta) < 0.01) {
+ ambientSound.sound->setBalance(ambientSound.targetBalance);
+ return false;
+ } else {
+ // Make sure the increment is not zero once converted to an integer
+ if (delta > 0 && delta < 1) {
+ delta = 1;
+ } else if (delta < 0 && delta > -1) {
+ delta = -1;
+ }
+
+ ambientSound.sound->setBalance(balance + delta);
+ return true;
+ }
+}
+
+RivenSound::RivenSound(MohawkEngine *vm, Audio::RewindableAudioStream *rewindStream) :
+ _vm(vm),
+ _volume(Audio::Mixer::kMaxChannelVolume),
+ _balance(0),
+ _looping(false),
+ _stream(rewindStream) {
+
+}
+
+bool RivenSound::isPlaying() const {
+ return _vm->_mixer->isSoundHandleActive(_handle);
+}
+
+void RivenSound::pause() {
+ _vm->_mixer->pauseHandle(_handle, true);
+}
+
+void RivenSound::setVolume(uint16 volume) {
+ _volume = volume;
+ if (isPlaying()) {
+ byte mixerVolume = convertVolume(volume);
+ _vm->_mixer->setChannelVolume(_handle, mixerVolume);
+ }
+}
+
+void RivenSound::setBalance(int16 balance) {
+ _balance = balance;
+ if (isPlaying()) {
+ int8 mixerBalance = convertBalance(balance);
+ _vm->_mixer->setChannelBalance(_handle, mixerBalance);
+ }
+}
+
+void RivenSound::setLooping(bool loop) {
+ if (isPlaying() && _looping != loop) {
+ warning("Changing loop state while a sound is playing is not implemented.");
+ }
+ _looping = loop;
+}
+
+void RivenSound::play() {
+ if (isPlaying()) {
+ // If the sound is already playing, make sure it is not paused
+ _vm->_mixer->pauseHandle(_handle, false);
+ return;
+ }
+
+ if (!_stream) {
+ warning("Trying to play a sound without a stream");
+ return;
+ }
+
+ Audio::AudioStream *playStream;
+ if (_looping) {
+ playStream = new Audio::LoopingAudioStream(_stream, 0);
+ } else {
+ playStream = _stream;
+ }
+
+ int8 mixerBalance = convertBalance(_balance);
+ byte mixerVolume = convertVolume(_volume);
+ _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, playStream, -1, mixerVolume, mixerBalance);
+ _stream = nullptr;
+}
+
+byte RivenSound::convertVolume(uint16 volume) {
+ // The volume is a fixed point value in the Mohawk part of the original engine.
+ // It's not clear what happens when it is higher than one.
+ return (volume > 255) ? 255 : volume;
+}
+
+int8 RivenSound::convertBalance(int16 balance) {
+ return (int8)(balance >> 8);
+}
+
+RivenSound::~RivenSound() {
+ _vm->_mixer->stopHandle(_handle);
+ delete _stream;
+}
+
+int16 RivenSound::getBalance() const {
+ return _balance;
+}
+
+uint16 RivenSound::getVolume() const {
+ return _volume;
+}
+
+RivenSoundManager::AmbientSound::AmbientSound() :
+ sound(nullptr),
+ targetVolume(0),
+ targetBalance(0) {
+
+}
+
+RivenSoundManager::AmbientSoundList::AmbientSoundList() :
+ fading(false),
+ suspend(false) {
+}
+
+} // End of namespace Mohawk