[Scummvm-git-logs] scummvm master -> a6a095716a44359832940ec5532e3dd81488bd55
NMIError
noreply at scummvm.org
Sat Mar 1 20:31:48 UTC 2025
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:
a6a095716a GOT: Add music playback
Commit: a6a095716a44359832940ec5532e3dd81488bd55
https://github.com/scummvm/scummvm/commit/a6a095716a44359832940ec5532e3dd81488bd55
Author: Coen Rampen (crampen at gmail.com)
Date: 2025-03-01T21:31:15+01:00
Commit Message:
GOT: Add music playback
Changed paths:
A dists/engine-data/got.aud
A engines/got/musicdriver.cpp
A engines/got/musicdriver.h
A engines/got/musicdriver_adlib.cpp
A engines/got/musicdriver_adlib.h
A engines/got/musicparser.cpp
A engines/got/musicparser.h
dists/engine-data/README
dists/engine-data/engine_data.mk
engines/got/game/back.cpp
engines/got/game/init.cpp
engines/got/got.cpp
engines/got/module.mk
engines/got/sound.cpp
engines/got/sound.h
engines/got/views/dialogs/main_menu.cpp
engines/got/views/game.cpp
engines/got/views/opening.cpp
diff --git a/dists/engine-data/README b/dists/engine-data/README
index 8cda4ec29f2..31fa1400a95 100644
--- a/dists/engine-data/README
+++ b/dists/engine-data/README
@@ -48,6 +48,9 @@ memory constraints.
got.gfx:
This is a set of graphics for the title screen and main menu that were embedded in the executable.
+got.aud:
+This is the title screen music that was embedded in the executable.
+
grim-patch.lab:
This file contains set of script patches for Grim Fandango game.
diff --git a/dists/engine-data/engine_data.mk b/dists/engine-data/engine_data.mk
index 3461515ad40..173d04da39b 100644
--- a/dists/engine-data/engine_data.mk
+++ b/dists/engine-data/engine_data.mk
@@ -18,6 +18,7 @@ DIST_FILES_LIST += dists/engine-data/drascula.dat
endif
ifdef ENABLE_GOT
DIST_FILES_LIST += dists/engine-data/got.gfx
+DIST_FILES_LIST += dists/engine-data/got.aud
endif
ifdef ENABLE_HADESCH
DIST_FILES_LIST += dists/engine-data/hadesch_translations.dat
diff --git a/dists/engine-data/got.aud b/dists/engine-data/got.aud
new file mode 100644
index 00000000000..c61d4da82bb
Binary files /dev/null and b/dists/engine-data/got.aud differ
diff --git a/engines/got/game/back.cpp b/engines/got/game/back.cpp
index 7fe56c2ac83..406ac84226f 100644
--- a/engines/got/game/back.cpp
+++ b/engines/got/game/back.cpp
@@ -103,9 +103,6 @@ void showLevel(const int newLevel) {
if (!_G(setup)._scrollFlag)
_G(currentLevel) = newLevel; // Force no scroll
- if (_G(currentMusic) != _G(levelMusic))
- _G(sound).musicPause();
-
switch (_G(newLevel) - _G(currentLevel)) {
case 0:
// Nothing to do
diff --git a/engines/got/game/init.cpp b/engines/got/game/init.cpp
index 4a2470bedee..55b016ddb7c 100644
--- a/engines/got/game/init.cpp
+++ b/engines/got/game/init.cpp
@@ -110,7 +110,6 @@ void initGame() {
_G(newLevel) = _G(currentLevel);
_G(scrn).load(_G(currentLevel));
showLevel(_G(currentLevel));
- _G(sound).musicPlay(_G(levelMusic), true);
g_vars->resetEndGameFlags();
_G(startup) = false;
diff --git a/engines/got/got.cpp b/engines/got/got.cpp
index 337c40af1be..e7351194d7c 100644
--- a/engines/got/got.cpp
+++ b/engines/got/got.cpp
@@ -207,10 +207,7 @@ bool GotEngine::canSaveGameStateCurrently(Common::U32String *msg) {
void GotEngine::syncSoundSettings() {
Engine::syncSoundSettings();
- const bool allSoundIsMuted = ConfMan.getBool("mute");
-
- _mixer->muteSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getBool("sfx_mute") || allSoundIsMuted);
- _mixer->muteSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getBool("music_mute") || allSoundIsMuted);
+ _G(sound).syncSoundSettings();
}
void GotEngine::pauseEngineIntern(bool pause) {
@@ -231,6 +228,13 @@ void GotEngine::pauseEngineIntern(bool pause) {
_G(thunderSnakeCounter) = 0;
+ if (pause) {
+ _G(sound).musicPause();
+ }
+ else {
+ _G(sound).musicResume();
+ }
+
Engine::pauseEngineIntern(pause);
}
diff --git a/engines/got/module.mk b/engines/got/module.mk
index 6343d4a1577..b98c688b6da 100644
--- a/engines/got/module.mk
+++ b/engines/got/module.mk
@@ -6,6 +6,9 @@ MODULE_OBJS = \
events.o \
messages.o \
metaengine.o \
+ musicdriver.o \
+ musicdriver_adlib.o \
+ musicparser.o \
sound.o \
vars.o \
data/actor.o \
diff --git a/engines/got/musicdriver.cpp b/engines/got/musicdriver.cpp
new file mode 100644
index 00000000000..46e13802d76
--- /dev/null
+++ b/engines/got/musicdriver.cpp
@@ -0,0 +1,88 @@
+/* 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 "got/musicdriver.h"
+
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/system.h"
+
+namespace Got {
+
+MusicDriver_Got::MusicDriver_Got(uint8 timerFrequency) : _isOpen(false), _timer_param(nullptr), _timer_proc(nullptr) {
+ setTimerFrequency(timerFrequency);
+}
+
+bool MusicDriver_Got::isOpen() const {
+ return _isOpen;
+}
+
+void MusicDriver_Got::setTimerFrequency(uint8 timerFrequency) {
+ assert(timerFrequency > 0);
+
+ _timerFrequency = timerFrequency;
+}
+
+void MusicDriver_Got::onTimer() {
+ if (_timer_proc && _timer_param)
+ _timer_proc(_timer_param);
+}
+
+void MusicDriver_Got::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
+ _timer_param = timer_param;
+ _timer_proc = timer_proc;
+}
+
+int MusicDriver_Got_NULL::open() {
+ // There is no output device, so register a timer to trigger the callbacks.
+ g_system->getTimerManager()->installTimerProc(timerCallback, 1000000 / _timerFrequency, this, "MusicDriver_Got_NULL");
+
+ _isOpen = true;
+
+ return 0;
+}
+
+void MusicDriver_Got_NULL::close() {
+ if (!_isOpen)
+ return;
+
+ g_system->getTimerManager()->removeTimerProc(timerCallback);
+
+ _isOpen = false;
+}
+
+void MusicDriver_Got_NULL::setTimerFrequency(uint8 timerFrequency) {
+ if (timerFrequency == _timerFrequency)
+ return;
+
+ MusicDriver_Got::setTimerFrequency(timerFrequency);
+
+ // Update the timer frequency.
+ g_system->getTimerManager()->removeTimerProc(timerCallback);
+ g_system->getTimerManager()->installTimerProc(timerCallback, 1000000 / _timerFrequency, this, "MusicDriver_Got_NULL");
+}
+
+void MusicDriver_Got_NULL::timerCallback(void *data) {
+ MusicDriver_Got_NULL *driver = (MusicDriver_Got_NULL *)data;
+ driver->onTimer();
+}
+
+} // namespace Got
diff --git a/engines/got/musicdriver.h b/engines/got/musicdriver.h
new file mode 100644
index 00000000000..d7b9a4cef87
--- /dev/null
+++ b/engines/got/musicdriver.h
@@ -0,0 +1,77 @@
+/* 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 GOT_MUSICDRIVER_H
+#define GOT_MUSICDRIVER_H
+
+#include "common/timer.h"
+
+namespace Got {
+
+class MusicDriver_Got {
+public:
+ MusicDriver_Got(uint8 timerFrequency);
+ virtual ~MusicDriver_Got() { };
+
+ virtual int open() = 0;
+ bool isOpen() const;
+ virtual void close() = 0;
+
+ virtual void syncSoundSettings() = 0;
+
+ virtual void send(uint16 b) = 0;
+ virtual void stopAllNotes() = 0;
+
+ virtual void setTimerFrequency(uint8 timerFrequency);
+ void onTimer();
+ void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
+
+protected:
+ // True if the driver has been successfully opened.
+ bool _isOpen;
+ // The number of timer callbacks per second.
+ int _timerFrequency;
+
+ // External timer callback
+ void *_timer_param;
+ Common::TimerManager::TimerProc _timer_proc;
+};
+
+class MusicDriver_Got_NULL : public MusicDriver_Got {
+public:
+ MusicDriver_Got_NULL(uint8 timerFrequency) : MusicDriver_Got(timerFrequency) { };
+ ~MusicDriver_Got_NULL() { };
+
+ int open() override;
+ void close() override;
+
+ void syncSoundSettings() override { };
+ void send(uint16 b) override { };
+ void stopAllNotes() override { };
+
+ void setTimerFrequency(uint8 timerFrequency) override;
+
+ static void timerCallback(void *data);
+};
+
+} // namespace Got
+
+#endif
diff --git a/engines/got/musicdriver_adlib.cpp b/engines/got/musicdriver_adlib.cpp
new file mode 100644
index 00000000000..3492a971670
--- /dev/null
+++ b/engines/got/musicdriver_adlib.cpp
@@ -0,0 +1,289 @@
+/* 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 "got/musicdriver_adlib.h"
+
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "audio/mididrv.h"
+
+
+namespace Got {
+
+MusicDriver_Got_AdLib::MusicDriver_Got_AdLib(uint8 timerFrequency) : MusicDriver_Got(timerFrequency),
+ _opl(nullptr),
+ _userMusicVolume(256),
+ _userMute(false) {
+ Common::fill(_channelOp0LevelRegisterValues, _channelOp0LevelRegisterValues + ARRAYSIZE(_channelOp0LevelRegisterValues), 0);
+ Common::fill(_channelOp1LevelRegisterValues, _channelOp1LevelRegisterValues + ARRAYSIZE(_channelOp1LevelRegisterValues), 0);
+ Common::fill(_channelBxRegisterValues, _channelBxRegisterValues + ARRAYSIZE(_channelBxRegisterValues), 0);
+ Common::fill(_channelConnectionValues, _channelConnectionValues + ARRAYSIZE(_channelConnectionValues), 0);
+}
+
+MusicDriver_Got_AdLib::~MusicDriver_Got_AdLib() {
+ close();
+}
+
+int MusicDriver_Got_AdLib::open() {
+ if (_isOpen)
+ return MidiDriver::MERR_ALREADY_OPEN;
+
+ int8 detectResult = OPL::Config::detect(OPL::Config::kOpl2);
+ if (detectResult == -1)
+ return MidiDriver::MERR_DEVICE_NOT_AVAILABLE;
+
+ // Create the emulator / hardware interface.
+ _opl = OPL::Config::create(OPL::Config::kOpl2);
+
+ if (_opl == nullptr)
+ return MidiDriver::MERR_CANNOT_CONNECT;
+
+ // Initialize emulator / hardware interface.
+ if (!_opl->init())
+ return MidiDriver::MERR_CANNOT_CONNECT;
+
+ _isOpen = true;
+
+ // Set default OPL register values.
+ initOpl();
+
+ // Start the emulator / hardware interface. This will also start the timer
+ // callbacks.
+ _opl->start(new Common::Functor0Mem<void, MusicDriver_Got_AdLib>(this, &MusicDriver_Got_AdLib::onTimer), _timerFrequency);
+
+ return 0;
+}
+
+void MusicDriver_Got_AdLib::close() {
+ if (!_isOpen)
+ return;
+
+ _isOpen = false;
+
+ stopAllNotes();
+
+ if (_opl) {
+ _opl->stop();
+ delete _opl;
+ _opl = nullptr;
+ }
+}
+
+void MusicDriver_Got_AdLib::syncSoundSettings() {
+ // Get user volume settings.
+ _userMusicVolume = MIN(256, ConfMan.getInt("music_volume"));
+ _userMute = ConfMan.getBool("mute") || ConfMan.getBool("music_mute");
+
+ // Apply the user volume.
+ recalculateVolumes();
+}
+
+void MusicDriver_Got_AdLib::send(uint16 b) {
+ uint8 oplRegister = b >> 8;
+ uint8 value = b & 0xFF;
+
+ if (oplRegister >= OPL_REGISTER_BASE_LEVEL && oplRegister < OPL_REGISTER_BASE_LEVEL + 0x20) {
+ // Write to a level register.
+
+ // Determine the channel and operator from the register number.
+ uint8 regOffset = oplRegister - OPL_REGISTER_BASE_LEVEL;
+ uint8 oplChannel = ((regOffset / 8) * 3) + ((regOffset % 8) % 3);
+ uint8 oplOperator = (regOffset % 8) / 3;
+ assert(oplChannel < OPL2_NUM_CHANNELS);
+ assert(oplOperator < 2);
+
+ // Store the new register value.
+ if (oplOperator == 0) {
+ _channelOp0LevelRegisterValues[oplChannel] = value;
+ } else {
+ _channelOp1LevelRegisterValues[oplChannel] = value;
+ }
+
+ // Apply user volume settings to the level.
+ uint8 scaledLevel = calculateVolume(oplChannel, oplOperator);
+ // Add the KSL bits to the new level value.
+ value = (value & 0xC0) | scaledLevel;
+ } else if ((oplRegister & 0xF0) == OPL_REGISTER_BASE_FNUMHIGH_BLOCK_KEYON) {
+ // Write to an F-num high / block / key on register.
+
+ uint8 oplChannel = oplRegister & 0x0F;
+ assert(oplChannel < OPL2_NUM_CHANNELS);
+
+ // Store the value, but clear the key on bit.
+ _channelBxRegisterValues[oplChannel] = value & 0x1F;
+ } else if ((oplRegister & 0xF0) == OPL_REGISTER_BASE_CONNECTION_FEEDBACK_PANNING) {
+ // Write to a connection register.
+
+ uint8 oplChannel = oplRegister & 0x0F;
+ assert(oplChannel < OPL2_NUM_CHANNELS);
+
+ // Store the connection bit.
+ _channelConnectionValues[oplChannel] = value & 0x01;
+ }
+
+ // Write the new register value to the OPL chip.
+ writeRegister(oplRegister, value);
+}
+
+void MusicDriver_Got_AdLib::stopAllNotes() {
+ // Clear the key on bit on all OPL channels.
+ for (int i = 0; i < OPL2_NUM_CHANNELS; i++) {
+ writeRegister(0xB0 | i, _channelBxRegisterValues[i]);
+ }
+}
+
+void MusicDriver_Got_AdLib::initOpl() {
+ // Clear test flags and enable waveform select.
+ writeRegister(0x01, 0x20);
+
+ // Clear, stop and mask the timers and reset the interrupt.
+ writeRegister(0x02, 0);
+ writeRegister(0x03, 0);
+ writeRegister(0x04, 0x60);
+ writeRegister(0x04, 0x80);
+
+ // Set note select mode 0 and disable CSM mode.
+ writeRegister(0x08, 0);
+
+ // Clear operator registers.
+ for (int i = 0; i < 5; i++) {
+ uint8 baseReg = 0;
+ switch (i) {
+ case 0:
+ baseReg = OPL_REGISTER_BASE_FREQMULT_MISC;
+ break;
+ case 1:
+ baseReg = OPL_REGISTER_BASE_LEVEL;
+ break;
+ case 2:
+ baseReg = OPL_REGISTER_BASE_DECAY_ATTACK;
+ break;
+ case 3:
+ baseReg = OPL_REGISTER_BASE_RELEASE_SUSTAIN;
+ break;
+ case 4:
+ baseReg = OPL_REGISTER_BASE_WAVEFORMSELECT;
+ break;
+ }
+
+ for (int j = 0; j < OPL2_NUM_CHANNELS; j++) {
+ writeRegister(baseReg + determineOperatorRegisterOffset(j, 0), 0);
+ writeRegister(baseReg + determineOperatorRegisterOffset(j, 1), 0);
+ }
+ }
+
+ // Clear channel registers.
+ for (int i = 0; i < 3; i++) {
+ uint8 baseReg = 0;
+ switch (i) {
+ case 0:
+ baseReg = OPL_REGISTER_BASE_FNUMLOW;
+ break;
+ case 1:
+ baseReg = OPL_REGISTER_BASE_FNUMHIGH_BLOCK_KEYON;
+ break;
+ case 2:
+ baseReg = OPL_REGISTER_BASE_CONNECTION_FEEDBACK_PANNING;
+ break;
+ }
+
+ for (int j = 0; j < OPL2_NUM_CHANNELS; j++) {
+ writeRegister(baseReg + j, 0);
+ }
+ }
+
+ // Disable rhythm mode and set modulation and vibrato depth to low.
+ writeRegister(0xBD, 0);
+}
+
+void MusicDriver_Got_AdLib::recalculateVolumes() {
+ // Determine the value for all level registers and write them out.
+ for (int i = 0; i < OPL2_NUM_CHANNELS; i++) {
+ uint8 oplRegister = OPL_REGISTER_BASE_LEVEL + determineOperatorRegisterOffset(i, 0);
+ uint8 value = (_channelOp0LevelRegisterValues[i] & 0xC0) | calculateVolume(i, 0);
+ writeRegister(oplRegister, value);
+
+ oplRegister = OPL_REGISTER_BASE_LEVEL + determineOperatorRegisterOffset(i, 1);
+ value = (_channelOp1LevelRegisterValues[i] & 0xC0) | calculateVolume(i, 1);
+ writeRegister(oplRegister, value);
+ }
+}
+
+uint8 MusicDriver_Got_AdLib::calculateVolume(uint8 channel, uint8 operatorNum) {
+ // Get the last written level for this operator.
+ uint8 operatorDefVolume = (operatorNum == 0 ? _channelOp0LevelRegisterValues[channel] : _channelOp1LevelRegisterValues[channel]) & 0x3F;
+
+ // Determine if volume settings should be applied to this operator.
+ if (!isVolumeApplicableToOperator(channel, operatorNum))
+ // No need to apply volume settings; just use the level as written.
+ return operatorDefVolume;
+
+ uint8 invertedVolume = 0x3F - operatorDefVolume;
+
+ // Scale by user volume.
+ if (_userMute) {
+ invertedVolume = 0;
+ } else {
+ invertedVolume = (invertedVolume * _userMusicVolume) >> 8;
+ }
+ uint8 scaledVolume = 0x3F - invertedVolume;
+
+ return scaledVolume;
+}
+
+bool MusicDriver_Got_AdLib::isVolumeApplicableToOperator(uint8 channel, uint8 operatorNum) {
+ // 2 operator instruments have 2 different operator connections:
+ // additive (0x01) or FM (0x00) synthesis. Carrier operators in FM
+ // synthesis and both operators in additive synthesis need to have
+ // volume settings applied; modulator operators just use the level
+ // as written. In FM synthesis connection, operator 1 is a carrier.
+ return _channelConnectionValues[channel] == 0x01 || operatorNum == 1;
+}
+
+uint16 MusicDriver_Got_AdLib::determineOperatorRegisterOffset(uint8 oplChannel, uint8 operatorNum) {
+ // 2 operator register offset for each channel and operator:
+ //
+ // Channel | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 4 | 5 | 3 | 4 | 5 | 6 | 7 | 8 | 6 | 7 | 8 |
+ // Operator | 0 | 1 | 0 | 1 | 0 | 1 |
+ // Register | 0 | 1 | 2 | 3 | 4 | 5 | 8 | 9 | A | B | C | D |10 |11 |12 |13 |14 |15 |
+ return (oplChannel / 3 * 8) + (oplChannel % 3) + (operatorNum * 3);
+}
+
+void MusicDriver_Got_AdLib::writeRegister(uint8 oplRegister, uint8 value) {
+ //debug("Writing register %X %X", oplRegister, value);
+
+ _opl->writeReg(oplRegister, value);
+}
+
+void MusicDriver_Got_AdLib::setTimerFrequency(uint8 timerFrequency) {
+ if (timerFrequency == _timerFrequency)
+ return;
+
+ MusicDriver_Got::setTimerFrequency(timerFrequency);
+
+ if (isOpen()) {
+ // Update OPL timer frequency.
+ _opl->stop();
+ _opl->start(new Common::Functor0Mem<void, MusicDriver_Got_AdLib>(this, &MusicDriver_Got_AdLib::onTimer), _timerFrequency);
+ }
+}
+
+} // namespace Got
diff --git a/engines/got/musicdriver_adlib.h b/engines/got/musicdriver_adlib.h
new file mode 100644
index 00000000000..74329134a26
--- /dev/null
+++ b/engines/got/musicdriver_adlib.h
@@ -0,0 +1,120 @@
+/* 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 GOT_MUSICDRIVER_ADLIB_H
+#define GOT_MUSICDRIVER_ADLIB_H
+
+#include "got/musicdriver.h"
+
+#include "common/scummsys.h"
+#include "common/timer.h"
+#include "audio/fmopl.h"
+
+namespace Got {
+
+class MusicDriver_Got_AdLib : public MusicDriver_Got {
+public:
+ /**
+ * The number of available channels on the OPL2 chip.
+ */
+ static const uint8 OPL2_NUM_CHANNELS = 9;
+
+ /**
+ * OPL operator base registers.
+ */
+ static const uint8 OPL_REGISTER_BASE_FREQMULT_MISC = 0x20;
+ static const uint8 OPL_REGISTER_BASE_LEVEL = 0x40;
+ static const uint8 OPL_REGISTER_BASE_DECAY_ATTACK = 0x60;
+ static const uint8 OPL_REGISTER_BASE_RELEASE_SUSTAIN = 0x80;
+ static const uint8 OPL_REGISTER_BASE_WAVEFORMSELECT = 0xE0;
+
+ /**
+ * OPL channel base registers.
+ */
+ static const uint8 OPL_REGISTER_BASE_FNUMLOW = 0xA0;
+ static const uint8 OPL_REGISTER_BASE_FNUMHIGH_BLOCK_KEYON = 0xB0;
+ static const uint8 OPL_REGISTER_BASE_CONNECTION_FEEDBACK_PANNING = 0xC0;
+
+ MusicDriver_Got_AdLib(uint8 timerFrequency);
+ ~MusicDriver_Got_AdLib();
+
+ int open() override;
+ void close() override;
+
+ void syncSoundSettings() override;
+
+ void send(uint16 b) override;
+ void stopAllNotes() override;
+
+ void setTimerFrequency(uint8 timerFrequency) override;
+
+protected:
+ /**
+ * Initializes the OPL registers to their default values.
+ */
+ void initOpl();
+
+ void recalculateVolumes();
+ uint8 calculateVolume(uint8 channel, uint8 operatorNum);
+ /**
+ * Determines if volume settings should be applied to the operator level.
+ * This depends on the type of the operator (carrier or modulator), which
+ * depends on the type of connection specified on the channel.
+ *
+ * @param oplChannel The OPL channel
+ * @param operatorNum The number of the operator (0-1)
+ * @return True if volume should be applied, false otherwise
+ */
+ bool isVolumeApplicableToOperator(uint8 oplChannel, uint8 operatorNum);
+ /**
+ * Determines the offset from a base register for the specified operator of
+ * the specified OPL channel.
+ * Add the offset to the base register to get the correct register for this
+ * operator and channel.
+ *
+ * @param oplChannel The OPL channel for which to determine the offset.
+ * @param operatorNum The operator for which to determine the offset (0-1).
+ * @return The offset to the base register for this operator.
+ */
+ uint16 determineOperatorRegisterOffset(uint8 oplChannel, uint8 operatorNum);
+ void writeRegister(uint8 oplRegister, uint8 value);
+
+ // The OPL emulator / hardware interface.
+ OPL::OPL *_opl;
+
+ // Last written value to the operator 0 level register for each channel.
+ uint8 _channelOp0LevelRegisterValues[OPL2_NUM_CHANNELS];
+ // Last written value to the operator 1 level register for each channel.
+ uint8 _channelOp1LevelRegisterValues[OPL2_NUM_CHANNELS];
+ // Last written value to the F-num high / block / key on register for each
+ // channel, with the key on bit cleared.
+ uint8 _channelBxRegisterValues[OPL2_NUM_CHANNELS];
+ // Last written value of the connection bit for each channel.
+ uint8 _channelConnectionValues[OPL2_NUM_CHANNELS];
+
+ // User volume settings.
+ uint16 _userMusicVolume;
+ bool _userMute;
+};
+
+} // namespace Got
+
+#endif
diff --git a/engines/got/musicparser.cpp b/engines/got/musicparser.cpp
new file mode 100644
index 00000000000..cedfbf5a304
--- /dev/null
+++ b/engines/got/musicparser.cpp
@@ -0,0 +1,195 @@
+/* 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 "got/musicparser.h"
+
+#include "common/endian.h"
+#include "common/textconsole.h"
+
+namespace Got {
+
+MusicParser_Got::MusicParser_Got() :
+ _pause(false),
+ _abortParse(false),
+ _currentDelta(0),
+ _driver(nullptr),
+ _track(nullptr),
+ _trackLength(0),
+ _playPos(nullptr),
+ _loopStart(-1) { }
+
+MusicParser_Got::~MusicParser_Got() {
+ stopPlaying();
+}
+
+void MusicParser_Got::sendToDriver(uint16 b) {
+ if (_driver == nullptr)
+ return;
+
+ _driver->send(b);
+}
+
+void MusicParser_Got::sendToDriver(byte reg, byte value) {
+ // OPL register and value are sent as a uint16 to the driver.
+ sendToDriver((reg << 8) | value);
+}
+
+void MusicParser_Got::allNotesOff() {
+ if (_driver == nullptr)
+ return;
+
+ _driver->stopAllNotes();
+}
+
+void MusicParser_Got::resetTracking() {
+ _playPos = nullptr;
+}
+
+bool MusicParser_Got::startPlaying() {
+ if (_track == nullptr || _pause)
+ return false;
+
+ if (_playPos == nullptr) {
+ _playPos = _track;
+ }
+
+ return true;
+}
+
+void MusicParser_Got::pausePlaying() {
+ if (isPlaying() && !_pause) {
+ _pause = true;
+ allNotesOff();
+ }
+}
+
+void MusicParser_Got::resumePlaying() {
+ _pause = false;
+}
+
+void MusicParser_Got::stopPlaying() {
+ if (isPlaying())
+ allNotesOff();
+ resetTracking();
+ _pause = false;
+}
+
+bool MusicParser_Got::isPlaying() {
+ return _playPos != nullptr;
+}
+
+void MusicParser_Got::setMusicDriver(MusicDriver_Got *driver) {
+ _driver = driver;
+}
+
+bool MusicParser_Got::loadMusic(byte* data, uint32 size) {
+ assert(size >= 5);
+
+ // Data starts with a word defining the loop point.
+ uint16 loopHeader = READ_LE_UINT16(data);
+ data += 2;
+
+ // Rest of the data is music events.
+ _track = data;
+ _trackLength = size - 2;
+
+ if (loopHeader == 0) {
+ // No loop point defined.
+ _loopStart = -1;
+ } else {
+ // Loop point is defined as the number of words from the start of the
+ // data (including the header).
+ _loopStart = (loopHeader - 1) * 2;
+ if ((uint32)_loopStart >= _trackLength) {
+ warning("MusicParser_Got::loadMusic - Music data has loop start point after track end");
+ _loopStart = -1;
+ }
+ }
+
+ return true;
+}
+
+void MusicParser_Got::unloadMusic() {
+ if (_track == nullptr)
+ // No music data loaded
+ return;
+
+ stopPlaying();
+ _abortParse = true;
+ _track = nullptr;
+}
+
+void MusicParser_Got::onTimer() {
+ if (_playPos == nullptr || !_driver || _pause)
+ return;
+
+ if (_currentDelta > 0) {
+ _currentDelta--;
+ if (_currentDelta > 0)
+ // More ticks to go to the next event.
+ return;
+ }
+
+ _abortParse = false;
+ while (!_abortParse && isPlaying() && _currentDelta == 0) {
+ assert((_playPos + 3) < (_track + _trackLength));
+
+ // An event consists of a delta, followed by an OPL register / value pair.
+
+ // A delta consists of 1 or 2 bytes. Bit 7 in the first byte indicates
+ // if a second byte is used.
+ uint16 newDelta = *_playPos++;
+ if ((newDelta & 0x80) > 0) {
+ assert((_playPos + 3) < (_track + _trackLength));
+ newDelta &= 0x7F;
+ newDelta <<= 8;
+ newDelta |= *_playPos++;
+ }
+ // The delta is the number of ticks from this event to the next event,
+ // not the number of ticks preceding this event.
+ _currentDelta = newDelta;
+
+ // Next, read the OPL register / value pair.
+ byte oplRegister = *_playPos++;
+ byte oplRegValue = *_playPos++;
+
+ if (oplRegister == 0 && oplRegValue == 0) {
+ // End of track is indicated by an event with OPL register and value 0.
+ if (_loopStart >= 0) {
+ // Continue playback at the loop point.
+ _playPos = _track + _loopStart;
+ }
+ else {
+ stopPlaying();
+ }
+ }
+ else {
+ // Write the specified OPL register value.
+ sendToDriver(oplRegister, oplRegValue);
+ }
+ }
+}
+
+void MusicParser_Got::timerCallback(void *data) {
+ ((MusicParser_Got *)data)->onTimer();
+}
+
+} // namespace Got
diff --git a/engines/got/musicparser.h b/engines/got/musicparser.h
new file mode 100644
index 00000000000..68dcf32fcf4
--- /dev/null
+++ b/engines/got/musicparser.h
@@ -0,0 +1,77 @@
+/* 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 GOT_MUSICPARSER_H
+#define GOT_MUSICPARSER_H
+
+#include "got/musicdriver_adlib.h"
+
+#include "common/scummsys.h"
+
+namespace Got {
+
+class MusicParser_Got {
+public:
+ MusicParser_Got();
+ ~MusicParser_Got();
+
+ bool startPlaying();
+ void pausePlaying();
+ void resumePlaying();
+ void stopPlaying();
+ bool isPlaying();
+
+ void setMusicDriver(MusicDriver_Got *driver);
+
+ bool loadMusic(byte *data, uint32 size);
+ void unloadMusic();
+
+ void onTimer();
+ static void timerCallback(void *data);
+
+protected:
+ void allNotesOff();
+ void sendToDriver(uint16 b);
+ void sendToDriver(byte reg, byte value);
+ void resetTracking();
+
+ bool _pause;
+ bool _abortParse;
+ // Number of ticks to the next event to process.
+ uint16 _currentDelta;
+
+ MusicDriver_Got *_driver;
+
+ // Points to start of current track data, or nullptr if no track is loaded.
+ byte *_track;
+ // The length of the track data.
+ uint32 _trackLength;
+ // The current play position in the track data, or nullptr if the parser is
+ // not playing.
+ byte *_playPos;
+ // The offset from the start of _track where playback should restart if the
+ // track has reached the end. -1 if there is no (valid) loop point defined.
+ int32 _loopStart;
+};
+
+} // namespace Got
+
+#endif
diff --git a/engines/got/sound.cpp b/engines/got/sound.cpp
index 4caaa9f5dbb..8fdbe0ab918 100644
--- a/engines/got/sound.cpp
+++ b/engines/got/sound.cpp
@@ -20,16 +20,23 @@
*/
#include "got/sound.h"
-#include "audio/decoders/raw.h"
-#include "audio/decoders/voc.h"
-#include "common/memstream.h"
+
#include "got/got.h"
+#include "got/musicdriver_adlib.h"
#include "got/utils/file.h"
+#include "common/config-manager.h"
+#include "common/memstream.h"
+#include "audio/mididrv.h"
+#include "audio/decoders/raw.h"
+#include "audio/decoders/voc.h"
+
namespace Got {
static const byte SOUND_PRIORITY[] = {1, 2, 3, 3, 3, 1, 4, 4, 4, 5, 4, 3, 1, 2, 2, 5, 1, 3, 1};
+static const char *MUSIC_MENU_DATA_FILENAME = "GOT.AUD";
+
Sound::Sound() {
for (int i = 0; i < 3; i++)
_bossSounds[i] = nullptr;
@@ -40,6 +47,23 @@ Sound::~Sound() {
for (int i = 0; i < 3; i++) {
delete(_bossSounds[i]);
}
+
+ musicStop();
+
+ if (_musicDriver != nullptr) {
+ _musicDriver->setTimerCallback(nullptr, nullptr);
+ _musicDriver->close();
+ }
+
+ if (_musicParser != nullptr) {
+ delete _musicParser;
+ }
+ if (_musicDriver != nullptr) {
+ delete _musicDriver;
+ }
+
+ if (_musicData != nullptr)
+ delete[] _musicData;
}
void Sound::load() {
@@ -52,6 +76,40 @@ void Sound::load() {
// Allocate memory and load sound data
_soundData = new byte[f.size() - 16 * 8];
f.read(_soundData, f.size() - 16 * 8);
+
+ // Initialize music.
+
+ // Check the type of music device that the user has configured.
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB);
+ MusicType deviceType = MidiDriver::getMusicType(dev);
+
+ // Initialize the appropriate driver.
+ switch (deviceType) {
+ case MT_ADLIB:
+ _musicDriver = new MusicDriver_Got_AdLib(MUSIC_TIMER_FREQUENCY_GAME);
+ break;
+ default:
+ // No support for music other than AdLib.
+ _musicDriver = new MusicDriver_Got_NULL(MUSIC_TIMER_FREQUENCY_GAME);
+ break;
+ }
+
+ // Initialize the parser.
+ _musicParser = new MusicParser_Got();
+
+ // Open the driver.
+ int returnCode = _musicDriver->open();
+ if (returnCode != 0) {
+ warning("Sound::load - Failed to open music driver - error code %d.", returnCode);
+ return;
+ }
+
+ // Apply user volume settings.
+ syncSoundSettings();
+
+ // Connect the driver and the parser.
+ _musicParser->setMusicDriver(_musicDriver);
+ _musicDriver->setTimerCallback(_musicParser, &_musicParser->timerCallback);
}
void Sound::setupBoss(const int num) {
@@ -84,14 +142,18 @@ void Sound::playSound(const int index, const bool override) {
if (index >= NUM_SOUNDS)
return;
+ byte newPriority = SOUND_PRIORITY[index];
+
// If a sound is playing, stop it unless there is a priority override
if (soundPlaying()) {
- if (!override && _currentPriority < SOUND_PRIORITY[index])
+ if (!override && _currentPriority < newPriority)
return;
g_engine->_mixer->stopHandle(_soundHandle);
}
+ _currentPriority = newPriority;
+
Common::MemoryReadStream *stream;
if (index >= 16) {
// Boss sounds are not stored in the normal sound data, it's in 3 buffers in _bossSounds.
@@ -122,71 +184,60 @@ bool Sound::soundPlaying() const {
}
void Sound::musicPlay(const char *name, bool override) {
- if (name != _currentMusic || override) {
- g_engine->_mixer->stopHandle(_musicHandle);
+ if (_currentMusic == nullptr || strcmp(name, _currentMusic) || override) {
+ _musicParser->stopPlaying();
+ _musicParser->unloadMusic();
+ delete[] _musicData;
+
_currentMusic = name;
- File file(name);
-
-#ifdef TODO
- // FIXME: Completely wrong. Don't know music format yet
- // Open it up for access
- Common::SeekableReadStream *f = file.readStream(file.size());
- Audio::AudioStream *audioStream = Audio::makeRawStream(
- f, 11025, 0, DisposeAfterUse::YES);
- g_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType,
- &_musicHandle, audioStream);
-
-#else
- warning("TODO: play_music %s", name);
-
-#if 0
- Common::DumpFile *outFile = new Common::DumpFile();
- const Common::String outName = Common::String::format("%s.dump", name);
- outFile->open(Common::Path(outName));
- byte *buffer = new byte[file.size()];
- file.read(buffer, file.size());
- outFile->write(buffer, file.size());
- outFile->finalize();
- outFile->close();
-#endif
-
- const int startLoop = file.readUint16LE();
-
- while (!file.eos()) {
- int delayAfter = file.readByte();
- if (delayAfter & 0x80)
- delayAfter = ((delayAfter & 0x7f) << 8) | file.readByte();
-
- const int reg = file.readByte();
- const int value = file.readByte();
- if (reg == 0 && value == 0) {
- debug(1, "End of song");
- break;
+ File file;
+ if (!strcmp(name, "MENU")) {
+ // Title menu music is embedded in the executable.
+ // It has been extracted and included with ScummVM.
+ if (!file.exists(MUSIC_MENU_DATA_FILENAME)) {
+ warning("Could not find %s", MUSIC_MENU_DATA_FILENAME);
+ return;
}
+ file.open(MUSIC_MENU_DATA_FILENAME);
+
+ // Title music uses an alternate timer frequency.
+ _musicDriver->setTimerFrequency(MUSIC_TIMER_FREQUENCY_TITLE);
+ } else {
+ file.open(name);
+
+ _musicDriver->setTimerFrequency(MUSIC_TIMER_FREQUENCY_GAME);
+ }
+
+ // Copy music data to local buffer and load it into the parser.
+ _musicData = new byte[file.size()];
+ file.read(_musicData, file.size());
- debug(1, "DelayAfter %d, OPL reg 0x%X, value %d", delayAfter, reg, value);
+ if (!_musicParser->loadMusic(_musicData, file.size())) {
+ warning("Could not load music track %s", name);
+ return;
}
- debug(1, "looping at pos %d", startLoop);
-#endif
+
+ //debug("Playing music track %s", name);
+ _musicParser->startPlaying();
}
}
void Sound::musicPause() {
- g_engine->_mixer->pauseHandle(_musicHandle, true);
+ _musicParser->pausePlaying();
}
void Sound::musicResume() {
- g_engine->_mixer->pauseHandle(_musicHandle, false);
+ _musicParser->resumePlaying();
}
void Sound::musicStop() {
- musicPause();
+ _musicParser->stopPlaying();
_currentMusic = nullptr;
}
bool Sound::musicIsOn() const {
- return g_engine->_mixer->isSoundHandleActive(_musicHandle);
+ return _musicParser->isPlaying();
}
const char *Sound::getMusicName(const int num) const {
@@ -290,6 +341,13 @@ const char *Sound::getMusicName(const int num) const {
return name;
}
+void Sound::syncSoundSettings() {
+ g_engine->_mixer->muteSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getBool("sfx_mute") || ConfMan.getBool("mute"));
+
+ if (_musicDriver)
+ _musicDriver->syncSoundSettings();
+}
+
void playSound(const int index, const bool override) {
_G(sound).playSound(index, override);
}
diff --git a/engines/got/sound.h b/engines/got/sound.h
index b65742a2119..a99faf79bbb 100644
--- a/engines/got/sound.h
+++ b/engines/got/sound.h
@@ -22,12 +22,13 @@
#ifndef GOT_SOUND_H
#define GOT_SOUND_H
-#include "audio/fmopl.h"
-
-#include "audio/mixer.h"
+#include "got/musicdriver.h"
+#include "got/musicparser.h"
#include "got/data/defines.h"
#include "got/gfx/gfx_chunks.h"
+#include "audio/mixer.h"
+
namespace Got {
enum {
@@ -56,15 +57,20 @@ enum {
class Sound {
private:
+ static const uint8 MUSIC_TIMER_FREQUENCY_GAME = 120;
+ static const uint8 MUSIC_TIMER_FREQUENCY_TITLE = 140;
+
byte *_soundData = nullptr;
byte *_bossSounds[3];
Header _digiSounds[NUM_SOUNDS];
Audio::SoundHandle _soundHandle;
- int _currentPriority = 0;
+ byte _currentPriority = 0;
int8 _currentBossLoaded = 0;
const char *_currentMusic = nullptr;
- Audio::SoundHandle _musicHandle;
+ byte *_musicData = nullptr;
+ MusicDriver_Got *_musicDriver = nullptr;
+ MusicParser_Got *_musicParser = nullptr;
const char *getMusicName(int num) const;
@@ -87,6 +93,8 @@ public:
void musicResume();
void musicStop();
bool musicIsOn() const;
+
+ void syncSoundSettings();
};
extern void playSound(int index, bool override);
diff --git a/engines/got/views/dialogs/main_menu.cpp b/engines/got/views/dialogs/main_menu.cpp
index 2798730a60e..752af9944cc 100644
--- a/engines/got/views/dialogs/main_menu.cpp
+++ b/engines/got/views/dialogs/main_menu.cpp
@@ -36,6 +36,8 @@ MainMenu::MainMenu() : SelectOption("MainMenu", "God of Thunder Menu",
bool MainMenu::msgFocus(const FocusMessage &msg) {
g_vars->resetEndGameFlags();
+ musicPlay("MENU", false);
+
return SelectOption::msgFocus(msg);
}
diff --git a/engines/got/views/game.cpp b/engines/got/views/game.cpp
index 41d19a6db3d..d646774a155 100644
--- a/engines/got/views/game.cpp
+++ b/engines/got/views/game.cpp
@@ -40,6 +40,7 @@ Game::Game() : View("Game") {
bool Game::msgFocus(const FocusMessage &msg) {
Gfx::loadPalette();
+ musicPlay(_G(levelMusic), false);
return View::msgFocus(msg);
}
diff --git a/engines/got/views/opening.cpp b/engines/got/views/opening.cpp
index 6a1f3ec6b76..49c7533fa5a 100644
--- a/engines/got/views/opening.cpp
+++ b/engines/got/views/opening.cpp
@@ -100,7 +100,9 @@ bool Opening::tick() {
} else if (_frameCtr == 41) {
_shakeX = 0;
redraw();
- } else if (_frameCtr == 150) {
+ } else if (_frameCtr == 100) {
+ musicPlay("MENU", true);
+ } else if (_frameCtr == 200) {
fadeOut();
replaceView("Credits", true);
}
More information about the Scummvm-git-logs
mailing list