aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk
diff options
context:
space:
mode:
authorPaul Gilbert2016-08-27 13:59:08 -0400
committerPaul Gilbert2016-08-27 13:59:08 -0400
commitf5ab3d1cd9eb3a884b0ab5d0b154a4f9cccc74b7 (patch)
tree771a8d2b3fddf96c17a1d81d42cb08dfba09d110 /engines/mohawk
parent873d555add9aaf5eb0d021518f5134142e2c2ff6 (diff)
parent5ea32efbb0ecb3e6b8336ad3c2edd3905ea5b89a (diff)
downloadscummvm-rg350-f5ab3d1cd9eb3a884b0ab5d0b154a4f9cccc74b7.tar.gz
scummvm-rg350-f5ab3d1cd9eb3a884b0ab5d0b154a4f9cccc74b7.tar.bz2
scummvm-rg350-f5ab3d1cd9eb3a884b0ab5d0b154a4f9cccc74b7.zip
Merge branch 'master' into xeen
Diffstat (limited to 'engines/mohawk')
-rw-r--r--engines/mohawk/console.cpp1
-rw-r--r--engines/mohawk/cstime.cpp3
-rw-r--r--engines/mohawk/cstime.h1
-rw-r--r--engines/mohawk/dialogs.cpp13
-rw-r--r--engines/mohawk/dialogs.h4
-rw-r--r--engines/mohawk/livingbooks.cpp3
-rw-r--r--engines/mohawk/livingbooks.h1
-rw-r--r--engines/mohawk/module.mk1
-rw-r--r--engines/mohawk/mohawk.cpp9
-rw-r--r--engines/mohawk/mohawk.h1
-rw-r--r--engines/mohawk/myst.cpp5
-rw-r--r--engines/mohawk/myst.h1
-rw-r--r--engines/mohawk/riven.cpp9
-rw-r--r--engines/mohawk/riven.h2
-rw-r--r--engines/mohawk/riven_external.cpp6
-rw-r--r--engines/mohawk/riven_graphics.cpp2
-rw-r--r--engines/mohawk/riven_scripts.cpp46
-rw-r--r--engines/mohawk/riven_sound.cpp459
-rw-r--r--engines/mohawk/riven_sound.h197
-rw-r--r--engines/mohawk/sound.cpp451
-rw-r--r--engines/mohawk/sound.h37
21 files changed, 865 insertions, 387 deletions
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index fd79e53b07..f08eee9677 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -42,6 +42,7 @@
#ifdef ENABLE_RIVEN
#include "mohawk/riven.h"
#include "mohawk/riven_external.h"
+#include "mohawk/riven_sound.h"
#endif
namespace Mohawk {
diff --git a/engines/mohawk/cstime.cpp b/engines/mohawk/cstime.cpp
index 3b26378819..b2889be714 100644
--- a/engines/mohawk/cstime.cpp
+++ b/engines/mohawk/cstime.cpp
@@ -54,6 +54,7 @@ MohawkEngine_CSTime::MohawkEngine_CSTime(OSystem *syst, const MohawkGameDescript
_console = 0;
_gfx = 0;
+ _sound = 0;
_cursor = 0;
_interface = 0;
_view = 0;
@@ -66,6 +67,7 @@ MohawkEngine_CSTime::~MohawkEngine_CSTime() {
delete _interface;
delete _view;
delete _console;
+ delete _sound;
delete _gfx;
delete _rnd;
}
@@ -75,6 +77,7 @@ Common::Error MohawkEngine_CSTime::run() {
_console = new CSTimeConsole(this);
_gfx = new CSTimeGraphics(this);
+ _sound = new Sound(this);
_cursor = new DefaultCursorManager(this, ID_CURS);
_interface = new CSTimeInterface(this);
diff --git a/engines/mohawk/cstime.h b/engines/mohawk/cstime.h
index bfb7daf945..393032aaa9 100644
--- a/engines/mohawk/cstime.h
+++ b/engines/mohawk/cstime.h
@@ -136,6 +136,7 @@ public:
Common::RandomSource *_rnd;
+ Sound *_sound;
CSTimeGraphics *_gfx;
bool _needsUpdate;
diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp
index 8c11e3a5e9..38be98dfec 100644
--- a/engines/mohawk/dialogs.cpp
+++ b/engines/mohawk/dialogs.cpp
@@ -92,7 +92,7 @@ enum {
MohawkOptionsDialog::MohawkOptionsDialog(MohawkEngine *vm) :
GUI::Dialog(0, 0, 360, 200),
- _vm(vm) {
+ _vm(vm), _loadSlot(-1) {
_loadButton = new GUI::ButtonWidget(this, 245, 25, 100, 25, _("~L~oad"), 0, kLoadCmd);
_saveButton = new GUI::ButtonWidget(this, 245, 60, 100, 25, _("~S~ave"), 0, kSaveCmd);
new GUI::ButtonWidget(this, 245, 95, 100, 25, _("~Q~uit"), 0, kQuitCmd);
@@ -112,6 +112,7 @@ MohawkOptionsDialog::~MohawkOptionsDialog() {
void MohawkOptionsDialog::open() {
GUI::Dialog::open();
+ _loadSlot = -1;
_loadButton->setEnabled(_vm->canLoadGameStateCurrently());
_saveButton->setEnabled(_vm->canSaveGameStateCurrently());
}
@@ -133,12 +134,14 @@ void MohawkOptionsDialog::save() {
}
void MohawkOptionsDialog::load() {
- int slot = _loadDialog->runModalWithCurrentTarget();
+ // Do not load the game state from insite the dialog loop to
+ // avoid mouse cursor glitches (see bug #7164). Instead store
+ // the slot to load and let the code exectuting the dialog do
+ // the load after the dialog finished running.
+ _loadSlot = _loadDialog->runModalWithCurrentTarget();
- if (slot >= 0) {
- _vm->loadGameState(slot);
+ if (_loadSlot >= 0)
close();
- }
}
void MohawkOptionsDialog::reflowLayout() {
diff --git a/engines/mohawk/dialogs.h b/engines/mohawk/dialogs.h
index 3cfb628f9d..99db641948 100644
--- a/engines/mohawk/dialogs.h
+++ b/engines/mohawk/dialogs.h
@@ -81,6 +81,8 @@ public:
virtual void open() override;
virtual void reflowLayout() override;
virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
+
+ int getLoadSlot() const {return _loadSlot;}
private:
MohawkEngine *_vm;
@@ -90,6 +92,8 @@ private:
GUI::SaveLoadChooser *_loadDialog;
GUI::SaveLoadChooser *_saveDialog;
+
+ int _loadSlot;
void save();
void load();
diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 5af8fac901..579e3792b3 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -144,6 +144,7 @@ MohawkEngine_LivingBooks::MohawkEngine_LivingBooks(OSystem *syst, const MohawkGa
_rnd = new Common::RandomSource("livingbooks");
+ _sound = NULL;
_page = NULL;
const Common::FSNode gameDataDir(ConfMan.get("path"));
@@ -158,6 +159,7 @@ MohawkEngine_LivingBooks::~MohawkEngine_LivingBooks() {
destroyPage();
delete _console;
+ delete _sound;
delete _gfx;
delete _rnd;
_bookInfoFile.clear();
@@ -182,6 +184,7 @@ Common::Error MohawkEngine_LivingBooks::run() {
error("Could not find xRes/yRes variables");
_gfx = new LBGraphics(this, _screenWidth, _screenHeight);
+ _sound = new Sound(this);
if (getGameType() != GType_LIVINGBOOKSV1)
_cursor = new LivingBooksCursorManager_v2();
diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index 1a265a1a02..cf67c1ee8e 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -714,6 +714,7 @@ public:
Common::RandomSource *_rnd;
+ Sound *_sound;
LBGraphics *_gfx;
bool _needsRedraw, _needsUpdate;
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index 83e541e3e4..3fc118d2b6 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -57,6 +57,7 @@ MODULE_OBJS += \
riven_graphics.o \
riven_saveload.o \
riven_scripts.o \
+ riven_sound.o \
riven_vars.o
endif
diff --git a/engines/mohawk/mohawk.cpp b/engines/mohawk/mohawk.cpp
index d740d9479a..b38409f9f1 100644
--- a/engines/mohawk/mohawk.cpp
+++ b/engines/mohawk/mohawk.cpp
@@ -40,14 +40,12 @@ MohawkEngine::MohawkEngine(OSystem *syst, const MohawkGameDescription *gamedesc)
// Setup mixer
syncSoundSettings();
- _sound = 0;
_video = 0;
_pauseDialog = 0;
_cursor = 0;
}
MohawkEngine::~MohawkEngine() {
- delete _sound;
delete _video;
delete _pauseDialog;
delete _cursor;
@@ -58,7 +56,6 @@ MohawkEngine::~MohawkEngine() {
}
Common::Error MohawkEngine::run() {
- _sound = new Sound(this);
_video = new VideoManager(this);
_pauseDialog = new PauseDialog(this, "The game is paused. Press any key to continue.");
@@ -66,14 +63,12 @@ Common::Error MohawkEngine::run() {
}
void MohawkEngine::pauseEngineIntern(bool pause) {
+ Engine::pauseEngineIntern(pause);
+
if (pause) {
_video->pauseVideos();
- _sound->pauseSound();
- _sound->pauseSLST();
} else {
_video->resumeVideos();
- _sound->resumeSound();
- _sound->resumeSLST();
_system->updateScreen();
}
}
diff --git a/engines/mohawk/mohawk.h b/engines/mohawk/mohawk.h
index ac91dca971..bc0d642bce 100644
--- a/engines/mohawk/mohawk.h
+++ b/engines/mohawk/mohawk.h
@@ -100,7 +100,6 @@ public:
bool hasFeature(EngineFeature f) const;
- Sound *_sound;
VideoManager *_video;
CursorManager *_cursor;
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index 633b67f7e9..3c00c1e11b 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -75,6 +75,7 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription
_curResource = -1;
_hoverResource = nullptr;
+ _sound = nullptr;
_gfx = nullptr;
_console = nullptr;
_scriptParser = nullptr;
@@ -88,6 +89,7 @@ MohawkEngine_Myst::~MohawkEngine_Myst() {
DebugMan.clearAllDebugChannels();
delete _gfx;
+ delete _sound;
delete _console;
delete _scriptParser;
delete _gameState;
@@ -220,6 +222,7 @@ Common::Error MohawkEngine_Myst::run() {
MohawkEngine::run();
_gfx = new MystGraphics(this);
+ _sound = new Sound(this);
_console = new MystConsole(this);
_gameState = new MystGameState(this, _saveFileMan);
_optionsDialog = new MystOptionsDialog(this);
@@ -309,6 +312,8 @@ Common::Error MohawkEngine_Myst::run() {
_canSafelySaveLoad = true;
runDialog(*_optionsDialog);
+ if (_optionsDialog->getLoadSlot() >= 0)
+ loadGameState(_optionsDialog->getLoadSlot());
_canSafelySaveLoad = false;
if (_needsPageDrop) {
diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h
index 0b249e5499..0491e853b6 100644
--- a/engines/mohawk/myst.h
+++ b/engines/mohawk/myst.h
@@ -200,6 +200,7 @@ public:
bool _showResourceRects;
+ Sound *_sound;
MystGraphics *_gfx;
MystGameState *_gameState;
MystScriptParser *_scriptParser;
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index aa168a38d8..b7c83c0ff8 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -34,8 +34,8 @@
#include "mohawk/riven_external.h"
#include "mohawk/riven_graphics.h"
#include "mohawk/riven_saveload.h"
+#include "mohawk/riven_sound.h"
#include "mohawk/dialogs.h"
-#include "mohawk/sound.h"
#include "mohawk/video.h"
#include "mohawk/console.h"
@@ -59,6 +59,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
_curStack = kStackUnknown;
_hotspots = nullptr;
_gfx = nullptr;
+ _sound = nullptr;
_externalScriptHandler = nullptr;
_rnd = nullptr;
_scriptMan = nullptr;
@@ -92,6 +93,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
}
MohawkEngine_Riven::~MohawkEngine_Riven() {
+ delete _sound;
delete _gfx;
delete _console;
delete _externalScriptHandler;
@@ -123,6 +125,7 @@ Common::Error MohawkEngine_Riven::run() {
SearchMan.add("arcriven.z", &_installerArchive, 0, false);
_gfx = new RivenGraphics(this);
+ _sound = new RivenSoundManager(this);
_console = new RivenConsole(this);
_saveLoad = new RivenSaveLoad(this, _saveFileMan);
_externalScriptHandler = new RivenExternal(this);
@@ -199,6 +202,7 @@ Common::Error MohawkEngine_Riven::run() {
void MohawkEngine_Riven::handleEvents() {
// Update background running things
checkTimer();
+ _sound->updateSLST();
bool needsUpdate = _gfx->runScheduledWaterEffects();
needsUpdate |= _video->updateMovies();
@@ -258,6 +262,8 @@ void MohawkEngine_Riven::handleEvents() {
break;
case Common::KEYCODE_F5:
runDialog(*_optionsDialog);
+ if (_optionsDialog->getLoadSlot() >= 0)
+ loadGameState(_optionsDialog->getLoadSlot());
updateZipMode();
break;
case Common::KEYCODE_r:
@@ -708,6 +714,7 @@ void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
uint32 startTime = _system->getMillis();
while (_system->getMillis() < startTime + ms && !shouldQuit()) {
+ _sound->updateSLST();
bool needsUpdate = _gfx->runScheduledWaterEffects();
needsUpdate |= _video->updateMovies();
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 3ea50bb38d..ce819ac970 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -41,6 +41,7 @@ class RivenExternal;
class RivenConsole;
class RivenSaveLoad;
class RivenOptionsDialog;
+class RivenSoundManager;
// Riven Stack Types
enum {
@@ -121,6 +122,7 @@ public:
MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc);
virtual ~MohawkEngine_Riven();
+ RivenSoundManager *_sound;
RivenGraphics *_gfx;
RivenExternal *_externalScriptHandler;
Common::RandomSource *_rnd;
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 00075039fe..125630445e 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -24,7 +24,7 @@
#include "mohawk/riven.h"
#include "mohawk/riven_external.h"
#include "mohawk/riven_graphics.h"
-#include "mohawk/sound.h"
+#include "mohawk/riven_sound.h"
#include "mohawk/video.h"
#include "gui/message.h"
@@ -2429,7 +2429,7 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
// Play the sound of not being able to move
_vm->_cursor->setCursor(kRivenHideCursor);
_vm->_system->updateScreen();
- _vm->_sound->playSoundBlocking(13);
+ _vm->_sound->playSound(13);
}
} else {
// We're not at the bottom, and we can move down again
@@ -2463,7 +2463,7 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
// Play the sound of not being able to move
_vm->_cursor->setCursor(kRivenHideCursor);
_vm->_system->updateScreen();
- _vm->_sound->playSoundBlocking(13);
+ _vm->_sound->playSound(13);
return;
}
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index db22dde22d..b583bc9710 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -23,6 +23,7 @@
#include "mohawk/resource.h"
#include "mohawk/riven.h"
#include "mohawk/riven_graphics.h"
+#include "mohawk/riven_sound.h"
#include "common/system.h"
#include "engines/util.h"
@@ -111,6 +112,7 @@ void RivenGraphics::drawPLST(uint16 x) {
void RivenGraphics::updateScreen(Common::Rect updateRect) {
if (_updatesEnabled) {
_vm->runUpdateScreenScript();
+ _vm->_sound->triggerDrawSound();
if (_dirtyScreen) {
_activatedPLSTs.clear();
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index caa235ec8b..3655452603 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -25,7 +25,7 @@
#include "mohawk/riven_external.h"
#include "mohawk/riven_graphics.h"
#include "mohawk/riven_scripts.h"
-#include "mohawk/sound.h"
+#include "mohawk/riven_sound.h"
#include "mohawk/video.h"
#include "common/memstream.h"
@@ -309,54 +309,44 @@ void RivenScript::switchCard(uint16 op, uint16 argc, uint16 *argv) {
// Command 3: play an SLST from the script
void RivenScript::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) {
- SLSTRecord slstRecord;
int offset = 0, j = 0;
+ uint16 soundCount = argv[offset++];
+ SLSTRecord slstRecord;
slstRecord.index = 0; // not set by the scripts, so we set it to 0
- slstRecord.sound_count = argv[0];
- slstRecord.sound_ids = new uint16[slstRecord.sound_count];
-
- offset = slstRecord.sound_count;
+ slstRecord.soundIds.resize(soundCount);
- for (j = 0; j < slstRecord.sound_count; j++)
- slstRecord.sound_ids[j] = argv[offset++];
- slstRecord.fade_flags = argv[offset++];
+ for (j = 0; j < soundCount; j++)
+ slstRecord.soundIds[j] = argv[offset++];
+ slstRecord.fadeFlags = argv[offset++];
slstRecord.loop = argv[offset++];
- slstRecord.global_volume = argv[offset++];
+ slstRecord.globalVolume = argv[offset++];
slstRecord.u0 = argv[offset++];
- slstRecord.u1 = argv[offset++];
+ slstRecord.suspend = argv[offset++];
- slstRecord.volumes = new uint16[slstRecord.sound_count];
- slstRecord.balances = new int16[slstRecord.sound_count];
- slstRecord.u2 = new uint16[slstRecord.sound_count];
+ slstRecord.volumes.resize(soundCount);
+ slstRecord.balances.resize(soundCount);
+ slstRecord.u2.resize(soundCount);
- for (j = 0; j < slstRecord.sound_count; j++)
+ for (j = 0; j < soundCount; j++)
slstRecord.volumes[j] = argv[offset++];
- for (j = 0; j < slstRecord.sound_count; j++)
+ for (j = 0; j < soundCount; j++)
slstRecord.balances[j] = argv[offset++]; // negative = left, 0 = center, positive = right
- for (j = 0; j < slstRecord.sound_count; j++)
+ for (j = 0; j < soundCount; j++)
slstRecord.u2[j] = argv[offset++]; // Unknown
// Play the requested sound list
_vm->_sound->playSLST(slstRecord);
- _vm->_activatedSLST = true;
-
- delete[] slstRecord.sound_ids;
- delete[] slstRecord.volumes;
- delete[] slstRecord.balances;
- delete[] slstRecord.u2;
}
// Command 4: play local tWAV resource (twav_id, volume, block)
void RivenScript::playSound(uint16 op, uint16 argc, uint16 *argv) {
- byte volume = Sound::convertRivenVolume(argv[1]);
+ uint16 volume = argv[1];
+ bool playOnDraw = argv[2] == 1;
- if (argv[2] == 1)
- _vm->_sound->playSoundBlocking(argv[0], volume);
- else
- _vm->_sound->playSound(argv[0], volume);
+ _vm->_sound->playSound(argv[0], volume, playOnDraw);
}
// Command 7: set variable value (variable, value)
diff --git a/engines/mohawk/riven_sound.cpp b/engines/mohawk/riven_sound.cpp
new file mode 100644
index 0000000000..10a23a0719
--- /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 < record.volumes.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
diff --git a/engines/mohawk/riven_sound.h b/engines/mohawk/riven_sound.h
new file mode 100644
index 0000000000..f673d1ee3f
--- /dev/null
+++ b/engines/mohawk/riven_sound.h
@@ -0,0 +1,197 @@
+/* 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.
+ *
+ */
+
+#ifndef MOHAWK_RIVEN_SOUND_H
+#define MOHAWK_RIVEN_SOUND_H
+
+#include "common/array.h"
+#include "common/str.h"
+
+#include "audio/mixer.h"
+
+namespace Audio {
+class RewindableAudioStream;
+}
+
+namespace Mohawk {
+
+class MohawkEngine;
+class RivenSound;
+
+/**
+ * Ambient sound list
+ */
+struct SLSTRecord {
+ uint16 index;
+ Common::Array<uint16> soundIds;
+ uint16 fadeFlags;
+ uint16 loop;
+ uint16 globalVolume;
+ uint16 u0;
+ uint16 suspend;
+ Common::Array<uint16> volumes;
+ Common::Array<int16> balances;
+ Common::Array<uint16> u2;
+};
+
+/**
+ * Sound manager for Riven
+ *
+ * The sound manager can play simulteaneously:
+ * - An effect sound
+ * - A list of ambient sounds
+ *
+ * The list of ambient sounds can be cross faded
+ * with the previously running ambient sounds.
+ */
+class RivenSoundManager {
+public:
+ RivenSoundManager(MohawkEngine *vm);
+ ~RivenSoundManager();
+
+ /**
+ * Play an effect sound
+ *
+ * @param id Sound ID in the stack
+ * @param volume Playback volume, between 0 and 255
+ * @param playOnDraw Start playing when the current card is drawn instead of immediatly
+ */
+ void playSound(uint16 id, uint16 volume = 255, bool playOnDraw = false);
+
+ /** Start playing the scheduled on-draw effect sound, if any. Called by the GraphicsManager. */
+ void triggerDrawSound();
+
+ /** Stop playing the current effect sound, if any */
+ void stopSound();
+
+ /** Start playing an ambient sound list */
+ void playSLST(const SLSTRecord &slstRecord);
+
+ /** Start playing an ambient sound list from a resource */
+ void playSLST(uint16 index, uint16 card);
+
+ /** Stop playing the current ambient sounds */
+ void stopAllSLST(bool fade = false);
+
+ /** Update the ambient sounds for fading. Called once per frame. */
+ void updateSLST();
+
+private:
+ struct AmbientSound {
+ RivenSound *sound;
+ uint16 targetVolume;
+ int16 targetBalance;
+
+ AmbientSound();
+ };
+
+ struct AmbientSoundList {
+ bool fading;
+ bool suspend;
+ Common::Array<AmbientSound> sounds;
+
+ AmbientSoundList();
+ };
+
+ enum FadeFlags {
+ kFadeOutPreviousSounds = 1,
+ kFadeInNewSounds = 2
+ };
+
+ MohawkEngine *_vm;
+
+ int16 _mainAmbientSoundId;
+ AmbientSoundList _ambientSounds;
+ AmbientSoundList _previousAmbientSounds;
+ uint32 _nextFadeUpdate;
+
+ RivenSound *_effect;
+ bool _effectPlayOnDraw;
+
+ Audio::RewindableAudioStream *makeAudioStream(uint16 id);
+
+ // Ambient sound management
+ void addAmbientSounds(const SLSTRecord &record);
+ void playAmbientSounds();
+ void pauseAmbientSounds();
+ void moveAmbientSoundsToPreviousSounds();
+ void freePreviousAmbientSounds();
+
+ // Ambient sound fading
+ void setTargetVolumes(const SLSTRecord &record);
+ void applyTargetVolumes();
+ void startFadingAmbientSounds(uint16 flags);
+ void fadeAmbientSoundList(AmbientSoundList &list);
+ bool fadeVolume(AmbientSound &ambientSound);
+ bool fadeBalance(AmbientSound &ambientSound);
+ void setAmbientLooping(bool loop);
+};
+
+/**
+ * A sound used internally by the SoundManager
+ */
+class RivenSound {
+public:
+ RivenSound(MohawkEngine *vm, Audio::RewindableAudioStream *rewindStream);
+ ~RivenSound();
+
+ /** Start playing the sound stream passed to the constructor */
+ void play();
+
+ /** Is the sound currently playing ar paused? */
+ bool isPlaying() const;
+
+ /** Pause the playback, the play method resumes */
+ void pause();
+
+ /** Get the current volume */
+ uint16 getVolume() const;
+
+ /** Change the playback volume */
+ void setVolume(uint16 volume);
+
+ /** Get the current balance */
+ int16 getBalance() const;
+
+ /** Change the balance */
+ void setBalance(int16 balance);
+
+ /** Set the sound to indefinitely loop. Must be called before startting the playback */
+ void setLooping(bool loop);
+
+private:
+ static byte convertVolume(uint16 volume);
+ static int8 convertBalance(int16 balance);
+
+ MohawkEngine *_vm;
+
+ Audio::SoundHandle _handle;
+ Audio::RewindableAudioStream *_stream;
+
+ uint16 _volume;
+ int16 _balance;
+ bool _looping;
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index 38cb0b3608..0711561068 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -37,6 +37,150 @@
namespace Mohawk {
+Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *stream, CueList *cueList) {
+ uint32 tag = 0;
+ ADPCMStatus adpcmStatus;
+ DataChunk dataChunk;
+ uint32 dataSize = 0;
+
+ memset(&dataChunk, 0, sizeof(DataChunk));
+
+ if (stream->readUint32BE() != ID_MHWK) // MHWK tag again
+ error ("Could not find tag 'MHWK'");
+
+ stream->readUint32BE(); // Skip size
+
+ if (stream->readUint32BE() != ID_WAVE)
+ error ("Could not find tag 'WAVE'");
+
+ while (!dataChunk.audioData) {
+ tag = stream->readUint32BE();
+
+ switch (tag) {
+ case ID_ADPC:
+ debug(2, "Found Tag ADPC");
+ // ADPCM Sound Only
+ //
+ // This is useful for seeking in the stream, and is actually quite brilliant
+ // considering some of the other things Broderbund did with the engine.
+ // Only Riven and CSTime are known to use ADPCM audio and only CSTime
+ // actually requires this for seeking. On the other hand, it may be interesting
+ // to look at that one Riven sample that uses the cue points.
+ //
+ // Basically, the sample frame from the cue list is looked up here and then
+ // sets the starting sample and step index at the point specified. Quite
+ // an elegant/efficient system, really.
+
+ adpcmStatus.size = stream->readUint32BE();
+ adpcmStatus.itemCount = stream->readUint16BE();
+ adpcmStatus.channels = stream->readUint16BE();
+ adpcmStatus.statusItems = new ADPCMStatus::StatusItem[adpcmStatus.itemCount];
+
+ assert(adpcmStatus.channels <= 2);
+
+ for (uint16 i = 0; i < adpcmStatus.itemCount; i++) {
+ adpcmStatus.statusItems[i].sampleFrame = stream->readUint32BE();
+
+ for (uint16 j = 0; j < adpcmStatus.channels; j++) {
+ adpcmStatus.statusItems[i].channelStatus[j].last = stream->readSint16BE();
+ adpcmStatus.statusItems[i].channelStatus[j].stepIndex = stream->readUint16BE();
+ }
+ }
+
+ // TODO: Actually use this chunk. For now, just delete the status items...
+ delete[] adpcmStatus.statusItems;
+ break;
+ case ID_CUE:
+ debug(2, "Found Tag Cue#");
+ // Cues are used for animation sync. There are a couple in Myst and
+ // Riven but are not used there at all.
+
+ if (!cueList) {
+ uint32 size = stream->readUint32BE();
+ stream->skip(size);
+ break;
+ }
+
+ cueList->size = stream->readUint32BE();
+ cueList->pointCount = stream->readUint16BE();
+
+ if (cueList->pointCount == 0)
+ debug(2, "Cue# chunk found with no points!");
+ else
+ debug(2, "Cue# chunk found with %d point(s)!", cueList->pointCount);
+
+ cueList->points.resize(cueList->pointCount);
+ for (uint16 i = 0; i < cueList->pointCount; i++) {
+ cueList->points[i].sampleFrame = stream->readUint32BE();
+
+ byte nameLength = stream->readByte();
+ cueList->points[i].name.clear();
+ for (byte j = 0; j < nameLength; j++)
+ cueList->points[i].name += stream->readByte();
+
+ // Realign to an even boundary
+ if (!(nameLength & 1))
+ stream->readByte();
+
+ debug (3, "Cue# chunk point %d (frame %d): %s", i, cueList->points[i].sampleFrame, cueList->points[i].name.c_str());
+ }
+ break;
+ case ID_DATA:
+ debug(2, "Found Tag DATA");
+ // We subtract 20 from the actual chunk size, which is the total size
+ // of the chunk's header
+ dataSize = stream->readUint32BE() - 20;
+ dataChunk.sampleRate = stream->readUint16BE();
+ dataChunk.sampleCount = stream->readUint32BE();
+ dataChunk.bitsPerSample = stream->readByte();
+ dataChunk.channels = stream->readByte();
+ dataChunk.encoding = stream->readUint16BE();
+ dataChunk.loopCount = stream->readUint16BE();
+ dataChunk.loopStart = stream->readUint32BE();
+ dataChunk.loopEnd = stream->readUint32BE();
+
+ // NOTE: We currently ignore all of the loop parameters here. Myst uses the
+ // loopCount variable but the loopStart and loopEnd are always 0 and the size of
+ // the sample. Myst ME doesn't use the Mohawk Sound format and just standard WAVE
+ // files and therefore does not contain any of this metadata and we have to specify
+ // whether or not to loop elsewhere.
+
+ dataChunk.audioData = stream->readStream(dataSize);
+ break;
+ default:
+ error ("Unknown tag found in 'tWAV' chunk -- '%s'", tag2str(tag));
+ }
+ }
+
+ // makeMohawkWaveStream always takes control of the original stream
+ delete stream;
+
+ // The sound in Myst uses raw unsigned 8-bit data
+ // The sound in the CD version of Riven is encoded in Intel DVI ADPCM
+ // The sound in the DVD version of Riven is encoded in MPEG-2 Layer II or Intel DVI ADPCM
+ if (dataChunk.encoding == kCodecRaw) {
+ byte flags = Audio::FLAG_UNSIGNED;
+
+ if (dataChunk.channels == 2)
+ flags |= Audio::FLAG_STEREO;
+
+ return Audio::makeRawStream(dataChunk.audioData, dataChunk.sampleRate, flags);
+ } else if (dataChunk.encoding == kCodecADPCM) {
+ uint32 blockAlign = dataChunk.channels * dataChunk.bitsPerSample / 8;
+ return Audio::makeADPCMStream(dataChunk.audioData, DisposeAfterUse::YES, dataSize, Audio::kADPCMDVI, dataChunk.sampleRate, dataChunk.channels, blockAlign);
+ } else if (dataChunk.encoding == kCodecMPEG2) {
+#ifdef USE_MAD
+ return Audio::makeMP3Stream(dataChunk.audioData, DisposeAfterUse::YES);
+#else
+ warning ("MAD library not included - unable to play MP2 audio");
+#endif
+ } else {
+ error ("Unknown Mohawk WAVE encoding %d", dataChunk.encoding);
+ }
+
+ return nullptr;
+}
+
Sound::Sound(MohawkEngine* vm) : _vm(vm) {
_midiDriver = NULL;
_midiParser = NULL;
@@ -47,7 +191,6 @@ Sound::Sound(MohawkEngine* vm) : _vm(vm) {
Sound::~Sound() {
stopSound();
- stopAllSLST();
stopBackgroundMyst();
if (_midiParser) {
@@ -234,300 +377,6 @@ void Sound::stopMidi() {
_midiParser->unloadMusic();
}
-byte Sound::convertRivenVolume(uint16 volume) {
- return (volume == 256) ? 255 : volume;
-}
-
-void Sound::playSLST(uint16 index, uint16 card) {
- Common::SeekableReadStream *slstStream = _vm->getResource(ID_SLST, card);
- SLSTRecord slstRecord;
- uint16 recordCount = slstStream->readUint16BE();
-
- for (uint16 i = 0; i < recordCount; i++) {
- slstRecord.index = slstStream->readUint16BE();
- slstRecord.sound_count = slstStream->readUint16BE();
- slstRecord.sound_ids = new uint16[slstRecord.sound_count];
-
- for (uint16 j = 0; j < slstRecord.sound_count; j++)
- slstRecord.sound_ids[j] = slstStream->readUint16BE();
-
- slstRecord.fade_flags = slstStream->readUint16BE();
- slstRecord.loop = slstStream->readUint16BE();
- slstRecord.global_volume = slstStream->readUint16BE();
- slstRecord.u0 = slstStream->readUint16BE(); // Unknown
-
- if (slstRecord.u0 > 1)
- warning("slstRecord.u0: %d non-boolean", slstRecord.u0);
-
- slstRecord.u1 = slstStream->readUint16BE(); // Unknown
-
- if (slstRecord.u1 != 0)
- warning("slstRecord.u1: %d non-zero", slstRecord.u1);
-
- slstRecord.volumes = new uint16[slstRecord.sound_count];
- slstRecord.balances = new int16[slstRecord.sound_count];
- slstRecord.u2 = new uint16[slstRecord.sound_count];
-
- for (uint16 j = 0; j < slstRecord.sound_count; j++)
- slstRecord.volumes[j] = slstStream->readUint16BE();
-
- for (uint16 j = 0; j < slstRecord.sound_count; j++)
- slstRecord.balances[j] = slstStream->readSint16BE(); // negative = left, 0 = center, positive = right
-
- for (uint16 j = 0; j < slstRecord.sound_count; 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[] slstRecord.sound_ids;
- delete[] slstRecord.volumes;
- delete[] slstRecord.balances;
- delete[] slstRecord.u2;
- delete slstStream;
- return;
- }
-
- delete[] slstRecord.sound_ids;
- delete[] slstRecord.volumes;
- delete[] slstRecord.balances;
- delete[] slstRecord.u2;
- }
-
- delete slstStream;
-
- // If we have no matching entries, we do nothing and just let
- // the previous ambient sounds continue.
-}
-
-void Sound::playSLST(SLSTRecord slstRecord) {
- // End old sounds
- for (uint16 i = 0; i < _currentSLSTSounds.size(); i++) {
- bool noLongerPlay = true;
- for (uint16 j = 0; j < slstRecord.sound_count; j++)
- if (_currentSLSTSounds[i].id == slstRecord.sound_ids[j])
- noLongerPlay = false;
- if (noLongerPlay)
- stopSLSTSound(i, (slstRecord.fade_flags & 1) != 0);
- }
-
- // Start new sounds
- for (uint16 i = 0; i < slstRecord.sound_count; i++) {
- bool alreadyPlaying = false;
- for (uint16 j = 0; j < _currentSLSTSounds.size(); j++) {
- if (_currentSLSTSounds[j].id == slstRecord.sound_ids[i])
- alreadyPlaying = true;
- }
- if (!alreadyPlaying) {
- playSLSTSound(slstRecord.sound_ids[i],
- (slstRecord.fade_flags & (1 << 1)) != 0,
- slstRecord.loop != 0,
- slstRecord.volumes[i],
- slstRecord.balances[i]);
- }
- }
-}
-
-void Sound::stopAllSLST(bool fade) {
- for (uint16 i = 0; i < _currentSLSTSounds.size(); i++) {
- // TODO: Fade out, if requested
- _vm->_mixer->stopHandle(*_currentSLSTSounds[i].handle);
- delete _currentSLSTSounds[i].handle;
- }
-
- _currentSLSTSounds.clear();
-}
-
-static int8 convertBalance(int16 balance) {
- return (int8)(balance >> 8);
-}
-
-void Sound::playSLSTSound(uint16 id, bool fade, bool loop, uint16 volume, int16 balance) {
- // WORKAROUND: Some Riven SLST entries have a volume of 0, so we just ignore them.
- if (volume == 0)
- return;
-
- SLSTSndHandle sndHandle;
- sndHandle.handle = new Audio::SoundHandle();
- sndHandle.id = id;
- _currentSLSTSounds.push_back(sndHandle);
-
- Audio::RewindableAudioStream *rewindStream = makeMohawkWaveStream(_vm->getResource(ID_TWAV, id));
-
- // Loop here if necessary
- Audio::AudioStream *audStream = rewindStream;
- if (loop)
- audStream = Audio::makeLoopingAudioStream(rewindStream, 0);
-
- // TODO: Handle fading, possibly just raise the volume of the channel in increments?
-
- _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, sndHandle.handle, audStream, -1, convertRivenVolume(volume), convertBalance(balance));
-}
-
-void Sound::stopSLSTSound(uint16 index, bool fade) {
- // TODO: Fade out, if requested
- _vm->_mixer->stopHandle(*_currentSLSTSounds[index].handle);
- delete _currentSLSTSounds[index].handle;
- _currentSLSTSounds.remove_at(index);
-}
-
-void Sound::pauseSLST() {
- for (uint16 i = 0; i < _currentSLSTSounds.size(); i++)
- _vm->_mixer->pauseHandle(*_currentSLSTSounds[i].handle, true);
-}
-
-void Sound::resumeSLST() {
- for (uint16 i = 0; i < _currentSLSTSounds.size(); i++)
- _vm->_mixer->pauseHandle(*_currentSLSTSounds[i].handle, false);
-}
-
-Audio::RewindableAudioStream *Sound::makeMohawkWaveStream(Common::SeekableReadStream *stream, CueList *cueList) {
- uint32 tag = 0;
- ADPCMStatus adpcmStatus;
- DataChunk dataChunk;
- uint32 dataSize = 0;
-
- memset(&dataChunk, 0, sizeof(DataChunk));
-
- if (stream->readUint32BE() != ID_MHWK) // MHWK tag again
- error ("Could not find tag 'MHWK'");
-
- stream->readUint32BE(); // Skip size
-
- if (stream->readUint32BE() != ID_WAVE)
- error ("Could not find tag 'WAVE'");
-
- while (!dataChunk.audioData) {
- tag = stream->readUint32BE();
-
- switch (tag) {
- case ID_ADPC:
- debug(2, "Found Tag ADPC");
- // ADPCM Sound Only
- //
- // This is useful for seeking in the stream, and is actually quite brilliant
- // considering some of the other things Broderbund did with the engine.
- // Only Riven and CSTime are known to use ADPCM audio and only CSTime
- // actually requires this for seeking. On the other hand, it may be interesting
- // to look at that one Riven sample that uses the cue points.
- //
- // Basically, the sample frame from the cue list is looked up here and then
- // sets the starting sample and step index at the point specified. Quite
- // an elegant/efficient system, really.
-
- adpcmStatus.size = stream->readUint32BE();
- adpcmStatus.itemCount = stream->readUint16BE();
- adpcmStatus.channels = stream->readUint16BE();
- adpcmStatus.statusItems = new ADPCMStatus::StatusItem[adpcmStatus.itemCount];
-
- assert(adpcmStatus.channels <= 2);
-
- for (uint16 i = 0; i < adpcmStatus.itemCount; i++) {
- adpcmStatus.statusItems[i].sampleFrame = stream->readUint32BE();
-
- for (uint16 j = 0; j < adpcmStatus.channels; j++) {
- adpcmStatus.statusItems[i].channelStatus[j].last = stream->readSint16BE();
- adpcmStatus.statusItems[i].channelStatus[j].stepIndex = stream->readUint16BE();
- }
- }
-
- // TODO: Actually use this chunk. For now, just delete the status items...
- delete[] adpcmStatus.statusItems;
- break;
- case ID_CUE:
- debug(2, "Found Tag Cue#");
- // Cues are used for animation sync. There are a couple in Myst and
- // Riven but are not used there at all.
-
- if (!cueList) {
- uint32 size = stream->readUint32BE();
- stream->skip(size);
- break;
- }
-
- cueList->size = stream->readUint32BE();
- cueList->pointCount = stream->readUint16BE();
-
- if (cueList->pointCount == 0)
- debug(2, "Cue# chunk found with no points!");
- else
- debug(2, "Cue# chunk found with %d point(s)!", cueList->pointCount);
-
- cueList->points.resize(cueList->pointCount);
- for (uint16 i = 0; i < cueList->pointCount; i++) {
- cueList->points[i].sampleFrame = stream->readUint32BE();
-
- byte nameLength = stream->readByte();
- cueList->points[i].name.clear();
- for (byte j = 0; j < nameLength; j++)
- cueList->points[i].name += stream->readByte();
-
- // Realign to an even boundary
- if (!(nameLength & 1))
- stream->readByte();
-
- debug (3, "Cue# chunk point %d (frame %d): %s", i, cueList->points[i].sampleFrame, cueList->points[i].name.c_str());
- }
- break;
- case ID_DATA:
- debug(2, "Found Tag DATA");
- // We subtract 20 from the actual chunk size, which is the total size
- // of the chunk's header
- dataSize = stream->readUint32BE() - 20;
- dataChunk.sampleRate = stream->readUint16BE();
- dataChunk.sampleCount = stream->readUint32BE();
- dataChunk.bitsPerSample = stream->readByte();
- dataChunk.channels = stream->readByte();
- dataChunk.encoding = stream->readUint16BE();
- dataChunk.loopCount = stream->readUint16BE();
- dataChunk.loopStart = stream->readUint32BE();
- dataChunk.loopEnd = stream->readUint32BE();
-
- // NOTE: We currently ignore all of the loop parameters here. Myst uses the
- // loopCount variable but the loopStart and loopEnd are always 0 and the size of
- // the sample. Myst ME doesn't use the Mohawk Sound format and just standard WAVE
- // files and therefore does not contain any of this metadata and we have to specify
- // whether or not to loop elsewhere.
-
- dataChunk.audioData = stream->readStream(dataSize);
- break;
- default:
- error ("Unknown tag found in 'tWAV' chunk -- '%s'", tag2str(tag));
- }
- }
-
- // makeMohawkWaveStream always takes control of the original stream
- delete stream;
-
- // The sound in Myst uses raw unsigned 8-bit data
- // The sound in the CD version of Riven is encoded in Intel DVI ADPCM
- // The sound in the DVD version of Riven is encoded in MPEG-2 Layer II or Intel DVI ADPCM
- if (dataChunk.encoding == kCodecRaw) {
- byte flags = Audio::FLAG_UNSIGNED;
-
- if (dataChunk.channels == 2)
- flags |= Audio::FLAG_STEREO;
-
- return Audio::makeRawStream(dataChunk.audioData, dataChunk.sampleRate, flags);
- } else if (dataChunk.encoding == kCodecADPCM) {
- uint32 blockAlign = dataChunk.channels * dataChunk.bitsPerSample / 8;
- return Audio::makeADPCMStream(dataChunk.audioData, DisposeAfterUse::YES, dataSize, Audio::kADPCMDVI, dataChunk.sampleRate, dataChunk.channels, blockAlign);
- } else if (dataChunk.encoding == kCodecMPEG2) {
-#ifdef USE_MAD
- return Audio::makeMP3Stream(dataChunk.audioData, DisposeAfterUse::YES);
-#else
- warning ("MAD library not included - unable to play MP2 audio");
-#endif
- } else {
- error ("Unknown Mohawk WAVE encoding %d", dataChunk.encoding);
- }
-
- return NULL;
-}
-
Audio::RewindableAudioStream *Sound::makeLivingBooksWaveStream_v1(Common::SeekableReadStream *stream) {
uint16 header = stream->readUint16BE();
uint16 rate = 0;
@@ -591,18 +440,6 @@ void Sound::stopSound(uint16 id) {
}
}
-void Sound::pauseSound() {
- for (uint32 i = 0; i < _handles.size(); i++)
- if (_handles[i].type == kUsedHandle)
- _vm->_mixer->pauseHandle(_handles[i].handle, true);
-}
-
-void Sound::resumeSound() {
- for (uint32 i = 0; i < _handles.size(); i++)
- if (_handles[i].type == kUsedHandle)
- _vm->_mixer->pauseHandle(_handles[i].handle, false);
-}
-
bool Sound::isPlaying(uint16 id) {
for (uint32 i = 0; i < _handles.size(); i++)
if (_handles[i].type == kUsedHandle && _handles[i].id == id)
diff --git a/engines/mohawk/sound.h b/engines/mohawk/sound.h
index f09706e155..2b4b1ce091 100644
--- a/engines/mohawk/sound.h
+++ b/engines/mohawk/sound.h
@@ -42,20 +42,6 @@ namespace Mohawk {
#define MAX_CHANNELS 2 // Can there be more than 2?
-struct SLSTRecord {
- uint16 index;
- uint16 sound_count;
- uint16 *sound_ids;
- uint16 fade_flags;
- uint16 loop;
- uint16 global_volume;
- uint16 u0;
- uint16 u1;
- uint16 *volumes;
- int16 *balances;
- uint16 *u2;
-};
-
enum SndHandleType {
kFreeHandle,
kUsedHandle
@@ -68,11 +54,6 @@ struct SndHandle {
uint16 id;
};
-struct SLSTSndHandle {
- Audio::SoundHandle *handle;
- uint16 id;
-};
-
struct ADPCMStatus { // Holds ADPCM status data, but is irrelevant for us.
uint32 size;
uint16 itemCount;
@@ -116,6 +97,8 @@ struct DataChunk {
Common::SeekableReadStream *audioData;
};
+Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *stream, CueList *cueList = nullptr);
+
class MohawkEngine;
class Sound {
@@ -130,8 +113,6 @@ public:
void stopMidi();
void stopSound();
void stopSound(uint16 id);
- void pauseSound();
- void resumeSound();
bool isPlaying(uint16 id);
bool isPlaying();
uint getNumSamplesPlayed(uint16 id);
@@ -144,21 +125,12 @@ public:
void stopBackgroundMyst();
void changeBackgroundVolumeMyst(uint16 vol);
- // Riven-specific sound functions
- void playSLST(uint16 index, uint16 card);
- void playSLST(SLSTRecord slstRecord);
- void pauseSLST();
- void resumeSLST();
- void stopAllSLST(bool fade = false);
- static byte convertRivenVolume(uint16 volume);
-
private:
MohawkEngine *_vm;
MidiDriver *_midiDriver;
MidiParser *_midiParser;
byte *_midiData;
- static Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *stream, CueList *cueList = NULL);
static Audio::RewindableAudioStream *makeLivingBooksWaveStream_v1(Common::SeekableReadStream *stream);
void initMidi();
@@ -169,11 +141,6 @@ private:
// Myst-specific
SndHandle _mystBackgroundSound;
-
- // Riven-specific
- void playSLSTSound(uint16 index, bool fade, bool loop, uint16 volume, int16 balance);
- void stopSLSTSound(uint16 id, bool fade);
- Common::Array<SLSTSndHandle> _currentSLSTSounds;
};
} // End of namespace Mohawk