[Scummvm-git-logs] scummvm master -> 92e7c6f3978f204e5a7a7e29cdda130d95dd0112
neuromancer
noreply at scummvm.org
Sun May 31 15:10:50 UTC 2026
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
fe6728af06 FREESCAPE: added adlib rendition music to driller (DOS)
92e7c6f397 SCUMM: RA: fixed missing SMUSH player header guards will break some builds
Commit: fe6728af06b9ed78ee557248ed95f26ab633f25e
https://github.com/scummvm/scummvm/commit/fe6728af06b9ed78ee557248ed95f26ab633f25e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-05-31T17:02:27+02:00
Commit Message:
FREESCAPE: added adlib rendition music to driller (DOS)
Changed paths:
A engines/freescape/games/driller/driller.musicdata.h
A engines/freescape/games/driller/opl.music.cpp
A engines/freescape/games/driller/opl.music.h
engines/freescape/detection.cpp
engines/freescape/games/driller/c64.music.cpp
engines/freescape/games/driller/dos.cpp
engines/freescape/games/driller/driller.cpp
engines/freescape/module.mk
diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 1504dc2b502..afba0909dbf 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -137,7 +137,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO5(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GUIO_RENDERHERCGREEN, GAMEOPTION_AUTOMATIC_DRILLING)
+ GUIO6(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GUIO_RENDERHERCGREEN, GAMEOPTION_AUTOMATIC_DRILLING, GAMEOPTION_OPL_MUSIC)
},
{
// Retail release
@@ -309,7 +309,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO4(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_AUTOMATIC_DRILLING)
+ GUIO5(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_AUTOMATIC_DRILLING, GAMEOPTION_OPL_MUSIC)
},
{
"spacestationoblivion",
diff --git a/engines/freescape/games/driller/c64.music.cpp b/engines/freescape/games/driller/c64.music.cpp
index 02c364f2dda..56213ce7367 100644
--- a/engines/freescape/games/driller/c64.music.cpp
+++ b/engines/freescape/games/driller/c64.music.cpp
@@ -23,12 +23,13 @@
#include "common/textconsole.h"
#include "common/util.h"
+#include "freescape/games/driller/driller.musicdata.h"
// --- Driller Music Data (Embedded from Disassembly) ---
namespace Freescape {
// Frequency Tables (0x0D53 - 0x0E12)
-const uint8_t frq_lo[96] = {
+extern const uint8_t frq_lo[96] = {
0x0C, 0x1C, 0x2D, 0x3E, 0x51, 0x66, 0x7B, 0x91, 0xA9, 0xC3, 0xDD, 0xFA, 0x18, 0x38, 0x5A, 0x7D, // 0D53
0xA3, 0xCC, 0xF6, 0x23, 0x53, 0x86, 0xBB, 0xF4, 0x30, 0x70, 0xB4, 0xFB, 0x47, 0x98, 0xED, 0x47, // 0D63
0xA7, 0x0C, 0x77, 0xE9, 0x61, 0xE1, 0x68, 0xF7, 0x8F, 0x30, 0xDA, 0x8F, 0x4E, 0x18, 0xEF, 0xD2, // 0D73
@@ -36,7 +37,7 @@ const uint8_t frq_lo[96] = {
0x3E, 0xC1, 0x6B, 0x3C, 0x39, 0x63, 0xBE, 0x4B, 0x0F, 0x0C, 0x45, 0xBF, 0x7D, 0x83, 0xD6, 0x79, // 0D93
0x73, 0xC7, 0x7C, 0x97, 0x1E, 0x18, 0x8B, 0x7E, 0xFA, 0x06, 0xAC, 0xF3, 0xE6, 0x8F, 0xF8, 0x2E // 0DA3
};
-const uint8_t frq_hi[96] = {
+extern const uint8_t frq_hi[96] = {
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, // 0DB3
0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, // 0DC3
0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0B, 0x0B, 0x0C, 0x0D, 0x0E, 0x0E, 0x0F, // 0DD3
@@ -47,7 +48,7 @@ const uint8_t frq_hi[96] = {
// Instrument Data (0x0EA5 - 0x1004) - Stored as flat arrays
// possibly_instrument_a0
-const uint8_t instrumentDataA0[] = {
+extern const uint8_t instrumentDataA0[] = {
0x00, 0x81, 0x0A, 0x00, 0x00, 0x00, 0x80, 0x01, // Inst 0
0x90, 0x41, 0xFE, 0x0D, 0x25, 0x00, 0x40, 0x02, // Inst 1
0x00, 0x81, 0xFD, 0x00, 0x00, 0x00, 0x80, 0x00, // Inst 2
@@ -72,7 +73,7 @@ const uint8_t instrumentDataA0[] = {
0xF1, 0x41, 0x0C, 0x00, 0x40, 0x00, 0x40, 0x06, // Inst 21 ('v')
};
// possibly_instrument_a1
-const uint8_t instrumentDataA1[] = {
+extern const uint8_t instrumentDataA1[] = {
0x00, 0x00, 0x11, 0x00, 0x00, 0x03, 0x00, 0x00, // Inst 0
0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, // Inst 1
0x06, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Inst 2
@@ -96,10 +97,10 @@ const uint8_t instrumentDataA1[] = {
0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, // Inst 20 ('u')
0x0A, 0x02, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, // Inst 21 ('v')
};
-const int NUM_INSTRUMENTS = sizeof(instrumentDataA0) / 8;
+extern const int NUM_INSTRUMENTS = sizeof(instrumentDataA0) / 8;
// Arpeggio Data (0x157A - 0x157E)
-const uint8_t arpeggio_data[] = {0x00, 0x0C, 0x18};
+extern const uint8_t arpeggio_data[] = {0x00, 0x0C, 0x18};
// Only one arpeggio table is defined (3 entries: +0, +12, +24 semitones)
// Music Data Pointers and Structures
@@ -157,55 +158,55 @@ const uint8_t pattern_40[] = {0xFA, 0x01, 0xFD, 0x7F, 0x17, 0x00, 0x00, 0x00, 0x
const uint8_t pattern_41[] = {0xFA, 0x01, 0xFD, 0x7F, 0x23, 0x00, 0x00, 0x00, 0xFF};
// Pattern Address Table (0x157F - 0x15D4)
-const uint8_t *const pattern_addresses[] = {
+extern const uint8_t *const pattern_addresses[] = {
pattern_00, pattern_01, pattern_03, pattern_02, pattern_04, pattern_05, pattern_06, pattern_07,
pattern_08, pattern_09, pattern_10, pattern_11, pattern_12, pattern_13, pattern_14, pattern_15,
pattern_16, pattern_17, pattern_18, pattern_19, pattern_20, pattern_21, pattern_22, pattern_23,
pattern_24, pattern_25, pattern_26, pattern_27, pattern_28, pattern_29, pattern_30, pattern_31,
pattern_32, pattern_33, pattern_34, pattern_35, pattern_36, pattern_37, pattern_38, pattern_39,
pattern_40, pattern_41};
-const int NUM_PATTERNS = ARRAYSIZE(pattern_addresses);
+extern const int NUM_PATTERNS = ARRAYSIZE(pattern_addresses);
// Tune Data (0x1054, 0x15D5 - 0x15E5)
-const uint8_t tune_tempo_data[] = {0x00, 0x03, 0x03}; // tempos for tune 0, 1, 2
-const uint8_t *const tune_track_data[][3] = {
+extern const uint8_t tune_tempo_data[] = {0x00, 0x03, 0x03}; // tempos for tune 0, 1, 2
+extern const uint8_t *const tune_track_data[][3] = {
{nullptr, nullptr, nullptr}, // Tune 0 (null pointers = stop)
{voice1_track_data, voice2_track_data, voice3_track_data}, // Tune 1
};
-const int NUM_TUNES = ARRAYSIZE(tune_track_data);
+extern const int NUM_TUNES = ARRAYSIZE(tune_track_data);
// SID Base Addresses for Voices
const int voice_sid_offset[] = {0, 7, 14};
-const uint8_t initialSomethingData[][3] = {
+extern const uint8_t initialSomethingData[][3] = {
{0x00, 0x00, 0x3F},
{0x00, 0x00, 0x3F},
{0x00, 0x00, 0x3F}
};
-const uint8_t initialInstrumentIndex[] = {0x08, 0x08, 0x20};
+extern const uint8_t initialInstrumentIndex[] = {0x08, 0x08, 0x20};
-const uint8_t initialSomethingElseData[][3] = {
+extern const uint8_t initialSomethingElseData[][3] = {
{0xBB, 0x90, 0x02},
{0xBB, 0x90, 0x02},
{0xF0, 0x90, 0x0C}
};
-const uint8_t initialCtrl0[] = {0x00, 0x00, 0x06};
-const uint8_t initialPwDirection[] = {0x00, 0x00, 0x01};
-const uint8_t initialStuffData[][7] = {
+extern const uint8_t initialCtrl0[] = {0x00, 0x00, 0x06};
+extern const uint8_t initialPwDirection[] = {0x00, 0x00, 0x01};
+extern const uint8_t initialStuffData[][7] = {
{0x47, 0x47, 0x06, 0x1F, 0x06, 0x00, 0x00},
{0x23, 0x23, 0x03, 0x13, 0x03, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
-const uint8_t initialThingsData[][7] = {
+extern const uint8_t initialThingsData[][7] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
-const uint8_t initialTwoCtr[] = {0x02, 0x02, 0x02};
+extern const uint8_t initialTwoCtr[] = {0x02, 0x02, 0x02};
// Debug log levels
#define DEBUG_LEVEL 4 // 0: Minimal, 1: Basic Flow, 2: Detailed State
diff --git a/engines/freescape/games/driller/dos.cpp b/engines/freescape/games/driller/dos.cpp
index 30ba3c74cec..29aaa90e2e4 100644
--- a/engines/freescape/games/driller/dos.cpp
+++ b/engines/freescape/games/driller/dos.cpp
@@ -20,9 +20,11 @@
*/
#include "common/file.h"
+#include "common/config-manager.h"
#include "freescape/freescape.h"
#include "freescape/games/driller/driller.h"
+#include "freescape/games/driller/opl.music.h"
#include "freescape/language/8bitDetokeniser.h"
namespace Freescape {
@@ -285,6 +287,9 @@ void DrillerEngine::loadAssetsDOSFullGame() {
_indicators[0]->convertToInPlace(_gfx->_texturePixelFormat);
_indicators[1]->convertToInPlace(_gfx->_texturePixelFormat);
}
+
+ if (ConfMan.getBool("opl_music"))
+ _playerMusic = new DrillerOPLMusicPlayer();
}
void DrillerEngine::loadAssetsDOSDemo() {
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index bb8b0e16a72..78b0837663a 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -267,8 +267,10 @@ void DrillerEngine::gotoArea(uint16 areaID, int entranceID) {
playSound(_soundIndexStart, true, _soundFxHandle);
} else {
playSound(_soundIndexStart, true, _soundFxHandle);
- // Start playing music, if any, in any supported format
- playMusic("Matt Gray - The Best Of Reformation - 07 Driller Theme");
+ if (_playerMusic)
+ _playerMusic->startMusic();
+ else
+ playMusic("Matt Gray - The Best Of Reformation - 07 Driller Theme");
}
} else if (areaID == 127) {
diff --git a/engines/freescape/games/driller/driller.musicdata.h b/engines/freescape/games/driller/driller.musicdata.h
new file mode 100644
index 00000000000..db76590fbc2
--- /dev/null
+++ b/engines/freescape/games/driller/driller.musicdata.h
@@ -0,0 +1,56 @@
+/* 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 FREESCAPE_DRILLER_MUSICDATA_H
+#define FREESCAPE_DRILLER_MUSICDATA_H
+
+#include "common/scummsys.h"
+
+namespace Freescape {
+
+extern const uint8_t frq_lo[96];
+extern const uint8_t frq_hi[96];
+
+extern const uint8_t instrumentDataA0[];
+extern const uint8_t instrumentDataA1[];
+extern const int NUM_INSTRUMENTS;
+
+extern const uint8_t arpeggio_data[];
+
+extern const uint8_t *const pattern_addresses[];
+extern const int NUM_PATTERNS;
+
+extern const uint8_t tune_tempo_data[];
+extern const uint8_t *const tune_track_data[][3];
+extern const int NUM_TUNES;
+
+extern const uint8_t initialSomethingData[][3];
+extern const uint8_t initialInstrumentIndex[];
+extern const uint8_t initialSomethingElseData[][3];
+extern const uint8_t initialCtrl0[];
+extern const uint8_t initialPwDirection[];
+extern const uint8_t initialStuffData[][7];
+extern const uint8_t initialThingsData[][7];
+extern const uint8_t initialTwoCtr[];
+
+} // namespace Freescape
+
+#endif
diff --git a/engines/freescape/games/driller/opl.music.cpp b/engines/freescape/games/driller/opl.music.cpp
new file mode 100644
index 00000000000..a6f95b2fb22
--- /dev/null
+++ b/engines/freescape/games/driller/opl.music.cpp
@@ -0,0 +1,766 @@
+/* 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 "engines/freescape/games/driller/opl.music.h"
+
+#include "common/textconsole.h"
+#include "common/util.h"
+#include "freescape/freescape.h"
+#include "freescape/games/driller/driller.musicdata.h"
+
+namespace Freescape {
+
+struct DrillerOPLBasePatch {
+ byte modChar;
+ byte carChar;
+ byte modLevel;
+ byte carLevel;
+ byte modAD;
+ byte carAD;
+ byte modSR;
+ byte carSR;
+ byte modWave;
+ byte carWave;
+ byte feedbackConnection;
+};
+
+const uint16 kDrillerOPLFreqs[] = {
+ 0x0000, 0x0168, 0x017D, 0x0194, 0x01AD, 0x01C5,
+ 0x01E1, 0x01FD, 0x021B, 0x023B, 0x025E, 0x0282,
+ 0x02A8, 0x02D0, 0x02FB, 0x0328, 0x0358, 0x038B,
+ 0x03C1, 0x03FA, 0x061B, 0x063C, 0x065E, 0x0682,
+ 0x06A7, 0x06D0, 0x06FB, 0x0728, 0x0758, 0x078B,
+ 0x07C1, 0x07FA, 0x0A1B, 0x0A3B, 0x0A5D, 0x0A81,
+ 0x0AA8, 0x0AD0, 0x0AFB, 0x0B2B, 0x0B58, 0x0B8B,
+ 0x0BC1, 0x0BFA, 0x0E1B, 0x0E3B, 0x0E5D, 0x0E81,
+ 0x0EA8, 0x0ED0, 0x0EFB, 0x0F28, 0x0F58, 0x0F8B,
+ 0x0FC1, 0x0FFA, 0x121B, 0x123B, 0x125D, 0x1281,
+ 0x12A8, 0x12D0, 0x12FB, 0x1328, 0x1358, 0x138B,
+ 0x13C1, 0x13FA, 0x161B, 0x163B, 0x1661, 0x1681,
+ 0x16A8, 0x16D0, 0x16FB, 0x1729, 0x1758, 0x178B,
+ 0x17C1, 0x17FA, 0x1A1B, 0x1A3B, 0x1A5D, 0x1A81,
+ 0x1AA8, 0x1AD0, 0x1AFB, 0x1B28, 0x1B58, 0x1B8B,
+ 0x1BC1, 0x1BFA, 0x1E1B, 0x1E3B, 0x1E5D
+};
+
+const byte kOPLModOffset[] = { 0x00, 0x01, 0x02 };
+const byte kOPLCarOffset[] = { 0x03, 0x04, 0x05 };
+
+const DrillerOPLBasePatch kDrillerOPLBasePatches[] = {
+ { 0x21, 0x21, 0x22, 0x04, 0xF2, 0xF3, 0x74, 0x45, 0x00, 0x00, 0x04 }, // triangle
+ { 0x02, 0x01, 0x1C, 0x00, 0xE3, 0xF2, 0x63, 0x35, 0x00, 0x01, 0x06 }, // pulse
+ { 0x31, 0x21, 0x25, 0x03, 0xD3, 0xE3, 0x64, 0x46, 0x00, 0x00, 0x02 }, // saw
+ { 0x01, 0x01, 0x2A, 0x08, 0xF4, 0xF2, 0x42, 0x31, 0x00, 0x00, 0x0E }, // noise/percussion
+ { 0x22, 0x21, 0x18, 0x00, 0xF4, 0xF2, 0x55, 0x36, 0x00, 0x00, 0x08 } // default lead
+};
+
+const byte kDrillerMusicAttenuation = 12;
+const byte kDrillerNoiseAttenuation = 0;
+const byte kDrillerNarrowPulseAttenuation = 6;
+const uint16 kDrillerNarrowPulseEdgeDistance = 0x0300;
+const byte kDrillerPulseBrightnessBoostMax = 24;
+const int kDrillerArpeggioSize = 3;
+
+byte attenuateDrillerOPLLevel(byte level, byte attenuation) {
+ return MIN<byte>((level & 0x3F) + attenuation, 0x3F) | (level & 0xC0);
+}
+
+byte getDrillerOPLAttenuation(byte control) {
+ return (control & 0x80) ? kDrillerNoiseAttenuation : kDrillerMusicAttenuation;
+}
+
+uint16 getDrillerSIDFrequency(uint8_t note) {
+ if (note >= 96)
+ note = 95;
+ return frq_lo[note] | (frq_hi[note] << 8);
+}
+
+byte getDrillerWaveformFamily(byte control) {
+ if (control & 0x80)
+ return 3;
+ if (control & 0x40)
+ return 1;
+ if (control & 0x20)
+ return 2;
+ if (control & 0x10)
+ return 0;
+ return 4;
+}
+
+byte getDrillerInstrumentControl(const uint8_t *instA0, const uint8_t *instA1, bool useAlternateControl) {
+ if (useAlternateControl && (instA1[2] & 0xF0))
+ return instA1[2];
+ if (instA0[1] & 0xF0)
+ return instA0[1];
+ if (instA0[6] & 0xF0)
+ return instA0[6];
+ if (instA1[2] & 0xF0)
+ return instA1[2];
+ return instA0[1];
+}
+
+void DrillerOPLMusicPlayer::VoiceState::reset() {
+ trackDataPtr = nullptr;
+ trackIndex = 0;
+ patternDataPtr = nullptr;
+ patternIndex = 0;
+ instrumentIndex = 0;
+ delayCounter = 0;
+ noteDuration = 0;
+ gateMask = 0xFF;
+ currentNote = 0;
+ currentNoteSlideTarget = 0;
+ currentControl = 0;
+ whatever0 = 0;
+ whatever1 = 0;
+ whatever2 = 0;
+ whatever3 = 0;
+ whatever4 = 0;
+ whatever2_vibDirToggle = 0;
+ portaStepRaw = 0;
+ memset(something_else, 0, sizeof(something_else));
+ ctrl0 = 0;
+ arpTableIndex = 0;
+ arpSpeedHiNibble = 0;
+ stuff_freq_porta_vib = 0;
+ stuff_freq_base = 0;
+ stuff_arp_counter = 0;
+ things_vib_state = 0;
+ things_vib_depth = 0;
+ things_vib_delay_reload = 0;
+ things_vib_delay_ctr = 0;
+ glideDownTimer = 0;
+ baseSIDFrequency = 0;
+ baseFrequencyFnum = 0;
+ baseFrequencyBlock = 0;
+ frequencyFnum = 0;
+ frequencyBlock = 0;
+ keyOn = false;
+ gateReleased = true;
+}
+
+DrillerOPLMusicPlayer::DrillerOPLMusicPlayer(int tuneIndex)
+ : _opl(nullptr),
+ _musicActive(false),
+ _targetTuneIndex(tuneIndex),
+ _globalTempo(3),
+ _globalTempoCounter(1),
+ _tick(0) {
+ _opl = OPL::Config::create();
+ if (!_opl || !_opl->init()) {
+ warning("DrillerOPLMusicPlayer: Failed to create OPL emulator");
+ delete _opl;
+ _opl = nullptr;
+ }
+}
+
+DrillerOPLMusicPlayer::~DrillerOPLMusicPlayer() {
+ stopMusic();
+ delete _opl;
+}
+
+void DrillerOPLMusicPlayer::startMusic() {
+ if (!_opl)
+ return;
+
+ stopMusic();
+ _opl->start(new Common::Functor0Mem<void, DrillerOPLMusicPlayer>(
+ this, &DrillerOPLMusicPlayer::onTimer), 50);
+ setupTune(_targetTuneIndex);
+}
+
+void DrillerOPLMusicPlayer::stopMusic() {
+ _musicActive = false;
+ if (_opl) {
+ silenceAll();
+ _opl->stop();
+ }
+}
+
+bool DrillerOPLMusicPlayer::isPlaying() const {
+ return _musicActive;
+}
+
+void DrillerOPLMusicPlayer::setupTune(int tuneIndex) {
+ if (tuneIndex < 1 || tuneIndex >= NUM_TUNES)
+ tuneIndex = 1;
+
+ silenceAll();
+ _tick = 0;
+ _globalTempo = tune_tempo_data[tuneIndex];
+ if (_globalTempo == 0)
+ _globalTempo = 1;
+ _globalTempoCounter = 1;
+
+ if (_opl) {
+ _opl->writeReg(0x01, 0x20);
+ _opl->writeReg(0xBD, 0x00);
+ }
+
+ const uint8_t *const *currentTuneTracks = tune_track_data[tuneIndex];
+ for (int i = 0; i < kChannelCount; ++i) {
+ _voiceState[i].reset();
+ _voiceState[i].trackDataPtr = currentTuneTracks ? currentTuneTracks[i] : nullptr;
+ }
+
+ resetVoices();
+ _musicActive = true;
+}
+
+void DrillerOPLMusicPlayer::resetVoices() {
+ for (int i = 0; i < kChannelCount; ++i) {
+ VoiceState &v = _voiceState[i];
+ if (!v.trackDataPtr) {
+ noteOff(i);
+ continue;
+ }
+
+ v.trackIndex = 0;
+ v.patternDataPtr = nullptr;
+ v.patternIndex = 0;
+ v.instrumentIndex = initialInstrumentIndex[i];
+ v.delayCounter = 0;
+ v.noteDuration = initialSomethingData[i][2];
+ v.gateMask = 0xFF;
+ v.currentNote = initialStuffData[i][3];
+ v.currentNoteSlideTarget = initialThingsData[i][6];
+ v.currentControl = 0;
+ v.whatever0 = 0;
+ v.whatever1 = 0;
+ v.whatever2 = 0;
+ v.whatever3 = 0;
+ v.whatever4 = 0;
+ v.whatever2_vibDirToggle = initialPwDirection[i];
+ v.portaStepRaw = initialSomethingData[i][0];
+ memcpy(v.something_else, initialSomethingElseData[i], sizeof(v.something_else));
+ v.ctrl0 = initialCtrl0[i];
+ v.arpTableIndex = 0;
+ v.arpSpeedHiNibble = initialStuffData[i][5];
+ v.stuff_freq_porta_vib = initialStuffData[i][0] | (initialStuffData[i][4] << 8);
+ v.stuff_freq_base = initialStuffData[i][1] | (initialStuffData[i][2] << 8);
+ v.stuff_arp_counter = initialStuffData[i][6];
+ v.things_vib_state = initialThingsData[i][0];
+ v.things_vib_depth = initialThingsData[i][1];
+ v.things_vib_delay_reload = initialThingsData[i][2];
+ v.things_vib_delay_ctr = initialThingsData[i][3];
+ v.glideDownTimer = initialTwoCtr[i];
+ v.baseSIDFrequency = 0;
+ v.keyOn = false;
+ v.gateReleased = true;
+ }
+}
+
+void DrillerOPLMusicPlayer::silenceAll() {
+ if (!_opl)
+ return;
+
+ _opl->writeReg(0xBD, 0x00);
+
+ for (int i = 0; i < kChannelCount; ++i) {
+ _voiceState[i].keyOn = false;
+ _voiceState[i].gateReleased = true;
+ _opl->writeReg(0xB0 + i, 0x00);
+ _opl->writeReg(0x40 + kOPLModOffset[i], 0x3F);
+ _opl->writeReg(0x40 + kOPLCarOffset[i], 0x3F);
+ }
+}
+
+void DrillerOPLMusicPlayer::onTimer() {
+ if (!_musicActive)
+ return;
+
+ for (int i = 0; i < kChannelCount; ++i)
+ playVoice(i);
+
+ _globalTempoCounter--;
+ if (_globalTempoCounter < 0)
+ _globalTempoCounter = _globalTempo;
+
+ _tick++;
+}
+
+void DrillerOPLMusicPlayer::playVoice(int channel) {
+ VoiceState &v = _voiceState[channel];
+ if (!v.trackDataPtr)
+ return;
+
+ int instBase = v.instrumentIndex;
+ if (instBase < 0 || instBase >= NUM_INSTRUMENTS * 8) {
+ instBase = 0;
+ v.instrumentIndex = 0;
+ }
+ const uint8_t *instA0 = &instrumentDataA0[instBase];
+ const uint8_t *instA1 = &instrumentDataA1[instBase];
+
+ if (instA0[7] & 0x04) {
+ bool useAlternateControl = false;
+ if (v.glideDownTimer != 0) {
+ v.glideDownTimer--;
+ useAlternateControl = instA1[2] != 0;
+ }
+
+ byte control = getDrillerInstrumentControl(instA0, instA1, useAlternateControl);
+ if (control != v.currentControl) {
+ v.currentControl = control;
+ debugC(1, kFreescapeDebugMedia,
+ "Driller OPL control tick=%u ch=%d inst=%u note=%u ctrl=%02x alt=%d",
+ _tick, channel, v.instrumentIndex / 8, v.currentNote, v.currentControl, useAlternateControl ? 1 : 0);
+ setOPLInstrument(channel, v);
+ }
+ }
+
+ if (_globalTempoCounter != 0) {
+ applyContinuousEffects(channel, v, instA0, instA1, false);
+ return;
+ }
+
+ v.delayCounter--;
+ if (v.delayCounter >= 0) {
+ applyContinuousEffects(channel, v, instA0, instA1, false);
+ return;
+ }
+
+ uint8_t patternNum = v.trackDataPtr[v.trackIndex];
+ if (patternNum == 0xFE) {
+ stopMusic();
+ return;
+ }
+
+ if (patternNum == 0xFF) {
+ v.trackIndex = 0;
+ patternNum = v.trackDataPtr[v.trackIndex];
+ if (patternNum == 0xFF || patternNum == 0xFE) {
+ stopMusic();
+ return;
+ }
+ }
+
+ if (patternNum >= NUM_PATTERNS) {
+ warning("Driller OPL: invalid pattern number %d on channel %d", patternNum, channel);
+ stopMusic();
+ return;
+ }
+
+ v.patternDataPtr = pattern_addresses[patternNum];
+ v.gateMask = 0xFF;
+ v.whatever2 = 0;
+ v.whatever1 = 0;
+ v.whatever0 = 0;
+
+ int safety = 128;
+ while (safety-- > 0) {
+ uint8_t cmd = v.patternDataPtr[v.patternIndex];
+
+ if (cmd >= 0xFD) {
+ v.patternIndex++;
+ v.noteDuration = v.patternDataPtr[v.patternIndex++];
+ continue;
+ }
+
+ if (cmd >= 0xFB) {
+ v.patternIndex++;
+ v.whatever2 = (cmd == 0xFB) ? 1 : 2;
+ v.portaStepRaw = v.patternDataPtr[v.patternIndex++];
+ v.whatever1 = 0;
+ v.whatever0 = 0;
+ continue;
+ }
+
+ if (cmd >= 0xFA) {
+ v.patternIndex++;
+ uint8_t instNum = v.patternDataPtr[v.patternIndex++];
+ if (instNum >= NUM_INSTRUMENTS)
+ instNum = 0;
+ v.instrumentIndex = instNum * 8;
+ instBase = v.instrumentIndex;
+ instA0 = &instrumentDataA0[instBase];
+ instA1 = &instrumentDataA1[instBase];
+
+ uint8_t instrumentByte = instA0[0];
+ v.something_else[0] = instrumentByte & 0xF0;
+ v.something_else[1] = instrumentByte & 0xF0;
+ v.something_else[2] = instrumentByte & 0x0F;
+ v.ctrl0 = instrumentByte & 0x0F;
+ v.currentControl = getDrillerInstrumentControl(instA0, instA1, false);
+ continue;
+ }
+
+ v.currentNote = cmd;
+ v.delayCounter = v.noteDuration;
+ v.whatever3 = 0;
+ v.whatever4 = 0;
+ v.glideDownTimer = 2;
+
+ applyNote(channel, v, instA0, instA1);
+
+ v.patternIndex++;
+ if (v.patternDataPtr[v.patternIndex] == 0xFF) {
+ v.patternIndex = 0;
+ v.trackIndex++;
+ uint8_t trackCmd = v.trackDataPtr[v.trackIndex];
+ if (trackCmd == 0xFF)
+ v.trackIndex = 0;
+ else if (trackCmd == 0xFE) {
+ stopMusic();
+ return;
+ }
+ }
+
+ ContinuousEffectEntry entry = postNoteEffectSetup(v, instA0, instA1);
+ if (entry == kFullEffectPath)
+ applyContinuousEffects(channel, v, instA0, instA1, false);
+ else if (entry == kPortamentoOnlyPath)
+ applyContinuousEffects(channel, v, instA0, instA1, true);
+ return;
+ }
+
+ warning("Driller OPL: parser safety stop on channel %d", channel);
+ noteOff(channel);
+ v.delayCounter = 12;
+}
+
+void DrillerOPLMusicPlayer::applyNote(int channel, VoiceState &v, const uint8_t *instA0, const uint8_t *instA1) {
+ uint8_t note = v.currentNote;
+
+ if (instA0[7] & 0x02) {
+ v.something_else[0] = v.something_else[1];
+ v.something_else[2] = v.ctrl0;
+ }
+
+ if (note == 0) {
+ note = v.currentNoteSlideTarget;
+ v.currentNote = note;
+ v.currentNoteSlideTarget = 0;
+ v.gateMask--;
+ noteOff(channel);
+ return;
+ }
+
+ if (note >= 96)
+ note = 95;
+
+ v.currentNote = note;
+ v.currentNoteSlideTarget = note;
+ v.stuff_freq_porta_vib = getDrillerSIDFrequency(note);
+ v.stuff_freq_base = v.stuff_freq_porta_vib;
+ v.baseSIDFrequency = v.stuff_freq_base;
+ v.currentControl = getDrillerInstrumentControl(instA0, instA1, false);
+ debugC(1, kFreescapeDebugMedia,
+ "Driller OPL note tick=%u ch=%d inst=%u note=%u ctrl=%02x track=%u pat=%u",
+ _tick, channel, v.instrumentIndex / 8, note, v.currentControl, v.trackIndex, v.patternIndex);
+ setOPLInstrument(channel, v);
+ noteOn(channel, v, note);
+}
+
+DrillerOPLMusicPlayer::ContinuousEffectEntry DrillerOPLMusicPlayer::postNoteEffectSetup(VoiceState &v, const uint8_t *instA0, const uint8_t *instA1) {
+ if (v.currentNoteSlideTarget == 0)
+ return kFullEffectPath;
+
+ if (v.whatever2 != 0)
+ return kPortamentoOnlyPath;
+
+ if (instA1[4] != 0) {
+ v.whatever2 = instA1[4];
+ v.portaStepRaw = instA1[3];
+ return kPortamentoOnlyPath;
+ }
+
+ if (instA1[0] != 0) {
+ if (instA0[5] != 0) {
+ v.arpTableIndex = instA0[5] & 0x0F;
+ v.arpSpeedHiNibble = (instA0[5] & 0xF0) >> 4;
+ v.stuff_arp_counter = 0;
+ v.whatever1 = 1;
+ v.whatever0 = 0;
+ return kVoiceDone;
+ }
+
+ v.whatever1 = 0;
+ v.things_vib_depth = instA1[0];
+ v.things_vib_delay_reload = instA1[1];
+ v.things_vib_delay_ctr = instA1[1];
+ v.things_vib_state = 0;
+ v.whatever0 = 1;
+ return kVoiceDone;
+ }
+
+ if (instA0[5] != 0) {
+ v.arpTableIndex = instA0[5] & 0x0F;
+ v.arpSpeedHiNibble = (instA0[5] & 0xF0) >> 4;
+ v.stuff_arp_counter = 0;
+ v.whatever1 = 1;
+ v.whatever0 = 0;
+ return kVoiceDone;
+ }
+
+ v.whatever1 = 0;
+ v.whatever0 = 0;
+ return kVoiceDone;
+}
+
+void DrillerOPLMusicPlayer::applyContinuousEffects(int channel, VoiceState &v, const uint8_t *instA0, const uint8_t *instA1, bool startAtPortamento) {
+ if (!startAtPortamento) {
+ uint8_t lfoSpeed = instA0[4];
+ if (lfoSpeed != 0) {
+ if (v.whatever2_vibDirToggle == 0) {
+ uint16_t sum = (uint16_t)v.something_else[0] + lfoSpeed;
+ v.something_else[0] = sum & 0xFF;
+ v.something_else[2] = (v.something_else[2] + (sum >> 8)) & 0xFF;
+ if (v.something_else[2] >= 0x0E)
+ v.whatever2_vibDirToggle++;
+ } else {
+ uint16_t diff = (uint16_t)v.something_else[0] - lfoSpeed;
+ uint8_t borrow = (diff > 0xFF) ? 1 : 0;
+ v.something_else[0] = diff & 0xFF;
+ v.something_else[2] = (v.something_else[2] - borrow) & 0xFF;
+ if (v.something_else[2] < 0x08)
+ v.whatever2_vibDirToggle--;
+ }
+ applyPulseWidth(channel, v);
+ }
+
+ if (v.whatever1) {
+ if (v.stuff_arp_counter == v.arpSpeedHiNibble)
+ v.stuff_arp_counter = 0;
+
+ uint8_t arpNote = v.currentNote;
+ if (v.stuff_arp_counter < kDrillerArpeggioSize) {
+ uint16_t noteWithOffset = v.currentNote + arpeggio_data[v.stuff_arp_counter];
+ arpNote = noteWithOffset >= 96 ? 95 : noteWithOffset;
+ }
+
+ writeNoteFrequency(channel, v, arpNote);
+ v.stuff_arp_counter++;
+ return;
+ }
+
+ if (v.whatever0) {
+ uint8_t freqLo = v.stuff_freq_porta_vib & 0xFF;
+ uint8_t freqHi = (v.stuff_freq_porta_vib >> 8) & 0xFF;
+
+ if (v.things_vib_state == 0 || v.things_vib_state >= 3) {
+ uint16_t diff = (uint16_t)freqLo - v.things_vib_depth;
+ uint8_t borrow = (diff > 0xFF) ? 1 : 0;
+ freqLo = diff & 0xFF;
+ freqHi = (freqHi - borrow) & 0xFF;
+ } else {
+ uint16_t sum = (uint16_t)freqLo + v.things_vib_depth;
+ freqLo = sum & 0xFF;
+ freqHi = (freqHi + (sum >> 8)) & 0xFF;
+ }
+
+ v.stuff_freq_porta_vib = freqLo | (freqHi << 8);
+ writeSIDFrequency(channel, v, v.stuff_freq_porta_vib);
+ v.things_vib_delay_ctr--;
+ if (v.things_vib_delay_ctr == 0) {
+ v.things_vib_delay_ctr = v.things_vib_delay_reload;
+ v.things_vib_state++;
+ if (v.things_vib_state >= 5)
+ v.things_vib_state = 1;
+ }
+ return;
+ }
+ }
+
+ if (v.whatever2 == 1) {
+ uint8_t freqLo = v.stuff_freq_porta_vib & 0xFF;
+ uint8_t freqHi = (v.stuff_freq_porta_vib >> 8) & 0xFF;
+ uint16_t diff = (uint16_t)freqLo - v.portaStepRaw - 1;
+ uint8_t borrow = (diff > 0xFF) ? 1 : 0;
+ freqLo = diff & 0xFF;
+ freqHi = (freqHi - borrow) & 0xFF;
+ v.stuff_freq_porta_vib = freqLo | (freqHi << 8);
+ writeSIDFrequency(channel, v, v.stuff_freq_porta_vib);
+ } else if (v.whatever2 == 2) {
+ uint8_t freqLo = v.stuff_freq_porta_vib & 0xFF;
+ uint8_t freqHi = (v.stuff_freq_porta_vib >> 8) & 0xFF;
+ uint16_t sum = (uint16_t)freqLo + v.portaStepRaw;
+ freqLo = sum & 0xFF;
+ freqHi = (freqHi + (sum >> 8)) & 0xFF;
+ v.stuff_freq_porta_vib = freqLo | (freqHi << 8);
+ writeSIDFrequency(channel, v, v.stuff_freq_porta_vib);
+ } else if (v.whatever2 == 3) {
+ uint8_t freqLo = v.stuff_freq_porta_vib & 0xFF;
+ uint8_t freqHi = ((v.stuff_freq_porta_vib >> 8) - v.portaStepRaw) & 0xFF;
+ v.stuff_freq_porta_vib = freqLo | (freqHi << 8);
+ writeSIDFrequency(channel, v, v.stuff_freq_porta_vib);
+ } else if (v.whatever2 != 0) {
+ uint8_t freqLo = v.stuff_freq_porta_vib & 0xFF;
+ uint8_t freqHi = ((v.stuff_freq_porta_vib >> 8) + v.portaStepRaw) & 0xFF;
+ v.stuff_freq_porta_vib = freqLo | (freqHi << 8);
+ writeSIDFrequency(channel, v, v.stuff_freq_porta_vib);
+ }
+
+ if (instA0[7] & 0x01)
+ applyHardRestart(channel, v, instA1);
+}
+
+void DrillerOPLMusicPlayer::applyHardRestart(int channel, VoiceState &v, const uint8_t *instA1) {
+ uint8_t storedHi = (v.stuff_freq_base >> 8) & 0xFF;
+ if (storedHi != 0) {
+ storedHi--;
+ v.stuff_freq_base = (v.stuff_freq_base & 0x00FF) | (storedHi << 8);
+ }
+
+ if (v.whatever3 != 0) {
+ v.whatever3--;
+ noteOff(channel);
+ return;
+ }
+
+ if (v.whatever4 != instA1[5]) {
+ v.whatever3++;
+ v.whatever4++;
+ } else {
+ v.whatever4 = 0;
+ v.whatever3 = 0;
+ }
+
+ v.stuff_freq_base = (v.stuff_freq_base & 0x00FF) | ((v.stuff_freq_porta_vib >> 8) << 8);
+ if (!v.keyOn && v.currentNote != 0) {
+ v.keyOn = true;
+ v.gateReleased = false;
+ writeSIDFrequency(channel, v, v.stuff_freq_porta_vib);
+ }
+}
+
+void DrillerOPLMusicPlayer::setOPLInstrument(int channel, VoiceState &v) {
+ if (!_opl)
+ return;
+
+ const DrillerOPLBasePatch &patch = kDrillerOPLBasePatches[getDrillerWaveformFamily(v.currentControl)];
+ byte mod = kOPLModOffset[channel];
+ byte car = kOPLCarOffset[channel];
+
+ _opl->writeReg(0x20 + mod, patch.modChar);
+ _opl->writeReg(0x20 + car, patch.carChar);
+ _opl->writeReg(0x60 + mod, patch.modAD);
+ _opl->writeReg(0x60 + car, patch.carAD);
+ _opl->writeReg(0x80 + mod, patch.modSR);
+ _opl->writeReg(0x80 + car, patch.carSR);
+ _opl->writeReg(0xE0 + mod, patch.modWave);
+ _opl->writeReg(0xE0 + car, patch.carWave);
+ _opl->writeReg(0xC0 + channel, patch.feedbackConnection);
+ applyPulseWidth(channel, v);
+}
+
+void DrillerOPLMusicPlayer::applyPulseWidth(int channel, const VoiceState &v) {
+ if (!_opl)
+ return;
+
+ const DrillerOPLBasePatch &patch = kDrillerOPLBasePatches[getDrillerWaveformFamily(v.currentControl)];
+ byte modLevel = patch.modLevel;
+ byte carLevel = patch.carLevel;
+ byte feedbackConnection = patch.feedbackConnection;
+ byte attenuation = getDrillerOPLAttenuation(v.currentControl);
+
+ if (v.currentControl & 0x40) {
+ uint16 pulseWidth = (v.something_else[0] | (v.something_else[2] << 8)) & 0x0FFF;
+ uint16 edgeDistance = MIN<uint16>(pulseWidth, 0x1000 - pulseWidth);
+ uint16 centerDistance = pulseWidth < 0x0800 ? 0x0800 - pulseWidth : pulseWidth - 0x0800;
+ byte brightnessBoost = MIN<byte>(centerDistance >> 5, kDrillerPulseBrightnessBoostMax);
+
+ modLevel = patch.modLevel > brightnessBoost ? patch.modLevel - brightnessBoost : 0;
+
+ byte feedback = (patch.feedbackConnection >> 1) & 0x07;
+ feedback = MIN<byte>(7, feedback + (centerDistance >> 8));
+ feedbackConnection = (patch.feedbackConnection & 0x01) | (feedback << 1);
+ if (edgeDistance <= kDrillerNarrowPulseEdgeDistance)
+ attenuation = kDrillerNarrowPulseAttenuation;
+ }
+
+ byte mod = kOPLModOffset[channel];
+ byte car = kOPLCarOffset[channel];
+ _opl->writeReg(0xC0 + channel, feedbackConnection);
+ _opl->writeReg(0x40 + mod, attenuateDrillerOPLLevel(modLevel, attenuation));
+ _opl->writeReg(0x40 + car, attenuateDrillerOPLLevel(carLevel, attenuation));
+}
+
+void DrillerOPLMusicPlayer::noteOn(int channel, VoiceState &v, uint8_t note) {
+ if (!_opl)
+ return;
+
+ v.keyOn = false;
+ writeFrequency(channel, v, v.frequencyFnum, v.frequencyBlock);
+
+ noteToFnumBlock(note, v.baseFrequencyFnum, v.baseFrequencyBlock);
+ v.baseSIDFrequency = getDrillerSIDFrequency(note);
+ v.keyOn = true;
+ v.gateReleased = false;
+ setFrequency(channel, v, v.baseFrequencyFnum, v.baseFrequencyBlock);
+}
+
+void DrillerOPLMusicPlayer::noteOff(int channel) {
+ if (!_opl)
+ return;
+
+ VoiceState &v = _voiceState[channel];
+ v.keyOn = false;
+ v.gateReleased = true;
+ writeFrequency(channel, v, v.frequencyFnum, v.frequencyBlock);
+}
+
+void DrillerOPLMusicPlayer::setFrequency(int channel, VoiceState &v, uint16 fnum, byte block) {
+ v.frequencyFnum = fnum;
+ v.frequencyBlock = block;
+ writeFrequency(channel, v, fnum, block);
+}
+
+void DrillerOPLMusicPlayer::writeFrequency(int channel, const VoiceState &v, uint16 fnum, byte block) {
+ if (!_opl)
+ return;
+
+ _opl->writeReg(0xA0 + channel, fnum & 0xFF);
+ byte b0 = ((fnum >> 8) & 0x03) | (block << 2);
+ if (v.keyOn)
+ b0 |= 0x20;
+ _opl->writeReg(0xB0 + channel, b0);
+}
+
+void DrillerOPLMusicPlayer::writeNoteFrequency(int channel, VoiceState &v, uint8_t note) {
+ uint16 fnum = 0;
+ byte block = 0;
+ noteToFnumBlock(note, fnum, block);
+ setFrequency(channel, v, fnum, block);
+}
+
+void DrillerOPLMusicPlayer::writeSIDFrequency(int channel, VoiceState &v, uint16 sidFrequency) {
+ if (v.baseSIDFrequency == 0 || v.baseFrequencyFnum == 0)
+ return;
+
+ uint32 scaledFnum = ((uint32)v.baseFrequencyFnum * sidFrequency + (v.baseSIDFrequency / 2)) / v.baseSIDFrequency;
+ byte block = v.baseFrequencyBlock;
+ while (scaledFnum > 0x3FF && block < 7) {
+ scaledFnum = (scaledFnum + 1) >> 1;
+ block++;
+ }
+
+ setFrequency(channel, v, MIN<uint32>(scaledFnum, 0x3FF), block);
+}
+
+void DrillerOPLMusicPlayer::noteToFnumBlock(int note, uint16 &fnum, byte &block) const {
+ note = CLIP<int>(note, 0, MIN<int>(kMaxNote, ARRAYSIZE(kDrillerOPLFreqs) - 1));
+ uint16 combined = kDrillerOPLFreqs[note];
+ fnum = combined & 0x03FF;
+ block = (combined >> 10) & 0x07;
+}
+
+} // namespace Freescape
diff --git a/engines/freescape/games/driller/opl.music.h b/engines/freescape/games/driller/opl.music.h
new file mode 100644
index 00000000000..ca3b6f69c01
--- /dev/null
+++ b/engines/freescape/games/driller/opl.music.h
@@ -0,0 +1,123 @@
+/* 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 FREESCAPE_DRILLER_OPL_MUSIC_H
+#define FREESCAPE_DRILLER_OPL_MUSIC_H
+
+#include "audio/fmopl.h"
+#include "freescape/music.h"
+
+namespace Freescape {
+
+class DrillerOPLMusicPlayer : public MusicPlayer {
+public:
+ DrillerOPLMusicPlayer(int tuneIndex = 1);
+ ~DrillerOPLMusicPlayer() override;
+
+ void startMusic() override;
+ void stopMusic() override;
+ bool isPlaying() const override;
+
+private:
+ enum {
+ kChannelCount = 3,
+ kMaxNote = 94
+ };
+
+ enum ContinuousEffectEntry {
+ kVoiceDone,
+ kFullEffectPath,
+ kPortamentoOnlyPath
+ };
+
+ struct VoiceState {
+ const uint8_t *trackDataPtr;
+ uint8_t trackIndex;
+ const uint8_t *patternDataPtr;
+ uint8_t patternIndex;
+ uint8_t instrumentIndex;
+ int8_t delayCounter;
+ uint8_t noteDuration;
+ uint8_t gateMask;
+ uint8_t currentNote;
+ uint8_t currentNoteSlideTarget;
+ uint8_t currentControl;
+ uint8_t whatever0;
+ uint8_t whatever1;
+ uint8_t whatever2;
+ uint8_t whatever3;
+ uint8_t whatever4;
+ uint8_t whatever2_vibDirToggle;
+ uint16_t portaStepRaw;
+ uint8_t something_else[3];
+ uint8_t ctrl0;
+ uint8_t arpTableIndex;
+ uint8_t arpSpeedHiNibble;
+ uint16_t stuff_freq_porta_vib;
+ uint16_t stuff_freq_base;
+ uint8_t stuff_arp_counter;
+ uint8_t things_vib_state;
+ uint16_t things_vib_depth;
+ uint8_t things_vib_delay_reload;
+ uint8_t things_vib_delay_ctr;
+ uint8_t glideDownTimer;
+ uint16_t baseSIDFrequency;
+ uint16_t baseFrequencyFnum;
+ uint8_t baseFrequencyBlock;
+ uint16_t frequencyFnum;
+ uint8_t frequencyBlock;
+ bool keyOn;
+ bool gateReleased;
+
+ void reset();
+ };
+
+ OPL::OPL *_opl;
+ bool _musicActive;
+ uint8_t _targetTuneIndex;
+ uint8_t _globalTempo;
+ int8_t _globalTempoCounter;
+ uint32 _tick;
+ VoiceState _voiceState[kChannelCount];
+
+ void onTimer();
+ void setupTune(int tuneIndex);
+ void resetVoices();
+ void silenceAll();
+ void playVoice(int channel);
+ void applyNote(int channel, VoiceState &voice, const uint8_t *instA0, const uint8_t *instA1);
+ ContinuousEffectEntry postNoteEffectSetup(VoiceState &voice, const uint8_t *instA0, const uint8_t *instA1);
+ void applyContinuousEffects(int channel, VoiceState &voice, const uint8_t *instA0, const uint8_t *instA1, bool startAtPortamento);
+ void applyHardRestart(int channel, VoiceState &voice, const uint8_t *instA1);
+ void setOPLInstrument(int channel, VoiceState &voice);
+ void applyPulseWidth(int channel, const VoiceState &voice);
+ void noteOn(int channel, VoiceState &voice, uint8_t note);
+ void noteOff(int channel);
+ void setFrequency(int channel, VoiceState &voice, uint16 fnum, byte block);
+ void writeFrequency(int channel, const VoiceState &voice, uint16 fnum, byte block);
+ void writeNoteFrequency(int channel, VoiceState &voice, uint8_t note);
+ void writeSIDFrequency(int channel, VoiceState &voice, uint16 sidFrequency);
+ void noteToFnumBlock(int note, uint16 &fnum, byte &block) const;
+};
+
+} // namespace Freescape
+
+#endif
diff --git a/engines/freescape/module.mk b/engines/freescape/module.mk
index ed47cb0019e..a88d1003f37 100644
--- a/engines/freescape/module.mk
+++ b/engines/freescape/module.mk
@@ -37,6 +37,7 @@ MODULE_OBJS := \
games/driller/cpc.o \
games/driller/dos.o \
games/driller/driller.o \
+ games/driller/opl.music.o \
games/driller/sounds.o \
games/driller/zx.o \
games/eclipse/amiga.o \
Commit: 92e7c6f3978f204e5a7a7e29cdda130d95dd0112
https://github.com/scummvm/scummvm/commit/92e7c6f3978f204e5a7a7e29cdda130d95dd0112
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-05-31T17:10:23+02:00
Commit Message:
SCUMM: RA: fixed missing SMUSH player header guards will break some builds
Changed paths:
engines/scumm/smush/rebel/smush_player_ra1.h
engines/scumm/smush/rebel/smush_player_ra2.h
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.h b/engines/scumm/smush/rebel/smush_player_ra1.h
index ff1dc1befdc..2188e206c62 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.h
+++ b/engines/scumm/smush/rebel/smush_player_ra1.h
@@ -19,7 +19,7 @@
*
*/
-#ifndef SCUMM_SMUSH_PLAYER_RA1_H
+#if !defined(SCUMM_SMUSH_PLAYER_RA1_H) && defined(ENABLE_SCUMM_7_8)
#define SCUMM_SMUSH_PLAYER_RA1_H
#include "scumm/smush/smush_player.h"
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 7a1004df06c..7af86cf66f0 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -19,7 +19,7 @@
*
*/
-#ifndef SCUMM_SMUSH_PLAYER_RA2_H
+#if !defined(SCUMM_SMUSH_PLAYER_RA2_H) && defined(ENABLE_SCUMM_7_8)
#define SCUMM_SMUSH_PLAYER_RA2_H
#include "scumm/smush/smush_player.h"
More information about the Scummvm-git-logs
mailing list