[Scummvm-git-logs] scummvm master -> a295fa5a0b7d6b46584d71b2a746d3b5536dc692

neuromancer noreply at scummvm.org
Mon May 4 21:34:55 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:
112d158f16 FREESCAPE: Add a common base class for the music players
a295fa5a0b FREESCAPE: Refactor Atari music code to be independent of the YM2149 implementation


Commit: 112d158f16d12ba4df0b8690ec5536fa81a896f2
    https://github.com/scummvm/scummvm/commit/112d158f16d12ba4df0b8690ec5536fa81a896f2
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-05-04T23:34:51+02:00

Commit Message:
FREESCAPE: Add a common base class for the music players

Changed paths:
  A engines/freescape/music.h
    engines/freescape/games/dark/c64.cpp
    engines/freescape/games/dark/c64.music.h
    engines/freescape/games/dark/dark.cpp
    engines/freescape/games/dark/dark.h
    engines/freescape/games/driller/c64.cpp
    engines/freescape/games/driller/c64.music.cpp
    engines/freescape/games/driller/c64.music.h
    engines/freescape/games/driller/driller.cpp
    engines/freescape/games/driller/driller.h
    engines/freescape/games/eclipse/ay.music.h
    engines/freescape/games/eclipse/c64.cpp
    engines/freescape/games/eclipse/c64.music.cpp
    engines/freescape/games/eclipse/c64.music.h
    engines/freescape/games/eclipse/cpc.cpp
    engines/freescape/games/eclipse/dos.cpp
    engines/freescape/games/eclipse/eclipse.cpp
    engines/freescape/games/eclipse/eclipse.h
    engines/freescape/games/eclipse/opl.music.h
    engines/freescape/games/eclipse/zx.cpp


diff --git a/engines/freescape/games/dark/c64.cpp b/engines/freescape/games/dark/c64.cpp
index 6b882b15fd4..25acf870909 100644
--- a/engines/freescape/games/dark/c64.cpp
+++ b/engines/freescape/games/dark/c64.cpp
@@ -350,7 +350,7 @@ void DarkEngine::loadAssetsC64FullGame() {
 	// the active player's SID is created.
 	_playerC64Sfx = new DarkSideC64SFXPlayer();
 	_playerC64Sfx->destroySID();
-	_playerC64Music = new DarkSideC64MusicPlayer();
+	_playerMusic = new DarkSideC64MusicPlayer();
 }
 
 void DarkEngine::playSoundC64(int index) {
@@ -363,14 +363,12 @@ void DarkEngine::toggleC64Sound() {
 	if (_c64UseSFX) {
 		if (_playerC64Sfx)
 			_playerC64Sfx->destroySID();
-		if (_playerC64Music) {
-			_playerC64Music->initSID();
-			_playerC64Music->startMusic();
-		}
+		if (_playerMusic)
+			_playerMusic->startMusic();
 		_c64UseSFX = false;
 	} else {
-		if (_playerC64Music)
-			_playerC64Music->destroySID();
+		if (_playerMusic)
+			_playerMusic->stopMusic();
 		if (_playerC64Sfx)
 			_playerC64Sfx->initSID();
 		_c64UseSFX = true;
diff --git a/engines/freescape/games/dark/c64.music.h b/engines/freescape/games/dark/c64.music.h
index 19d61343e6b..af5e2fa6493 100644
--- a/engines/freescape/games/dark/c64.music.h
+++ b/engines/freescape/games/dark/c64.music.h
@@ -23,21 +23,20 @@
 #define FREESCAPE_DARK_C64_MUSIC_H
 
 #include "audio/sid.h"
+#include "freescape/music.h"
 
 namespace Freescape {
 
 // 3-channel SID music player for Dark Side C64.
 // Implements the Wally Beben byte-stream sequencer from darkside.prg ($0901).
-class DarkSideC64MusicPlayer {
+class DarkSideC64MusicPlayer : public MusicPlayer {
 public:
 	DarkSideC64MusicPlayer();
 	~DarkSideC64MusicPlayer();
 
-	void startMusic();
-	void stopMusic();
-	bool isPlaying() const;
-	void initSID();
-	void destroySID();
+	void startMusic() override;
+	void stopMusic() override;
+	bool isPlaying() const override;
 
 private:
 	SID::SID *_sid;
@@ -67,6 +66,9 @@ private:
 
 	uint8 readPatByte(int ch);
 
+	void initSID();
+	void destroySID();
+
 	// Global state
 	bool _musicActive;
 	uint8 _speedDiv;       // $15A1: speed divider
diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index b8d3988e021..e38695c5419 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -35,7 +35,7 @@ namespace Freescape {
 
 DarkEngine::DarkEngine(OSystem *syst, const ADGameDescription *gd) : FreescapeEngine(syst, gd) {
 	_playerC64Sfx = nullptr;
-	_playerC64Music = nullptr;
+	_playerMusic = nullptr;
 	_c64UseSFX = false;
 	_c64CompassInitialized = false;
 	_c64CompassPosition = 0;
@@ -106,7 +106,7 @@ DarkEngine::DarkEngine(OSystem *syst, const ADGameDescription *gd) : FreescapeEn
 
 DarkEngine::~DarkEngine() {
 	delete _playerC64Sfx;
-	delete _playerC64Music;
+	delete _playerMusic;
 
 	for (auto &indicator : _cpcIndicators) {
 		indicator->free();
@@ -348,8 +348,8 @@ void DarkEngine::initGameState() {
 		}
 	}
 
-	if (isC64() && _playerC64Music)
-		_playerC64Music->startMusic();
+	if (isC64() && _playerMusic)
+		_playerMusic->startMusic();
 }
 
 void DarkEngine::loadAssets() {
diff --git a/engines/freescape/games/dark/dark.h b/engines/freescape/games/dark/dark.h
index e60926e1522..e16193463ff 100644
--- a/engines/freescape/games/dark/dark.h
+++ b/engines/freescape/games/dark/dark.h
@@ -21,6 +21,7 @@
 
 #include "audio/mixer.h"
 #include "common/array.h"
+#include "freescape/music.h"
 #include "freescape/games/dark/c64.music.h"
 #include "freescape/games/dark/c64.sfx.h"
 
@@ -143,7 +144,7 @@ public:
 	Audio::SoundHandle _soundFxHandleJetpack;
 
 	DarkSideC64SFXPlayer *_playerC64Sfx;
-	DarkSideC64MusicPlayer *_playerC64Music;
+	MusicPlayer *_playerMusic;
 	bool _c64UseSFX;
 	bool _c64CompassInitialized;
 	int _c64CompassPosition;
diff --git a/engines/freescape/games/driller/c64.cpp b/engines/freescape/games/driller/c64.cpp
index 0af0b4588e3..8f2bfd36c87 100644
--- a/engines/freescape/games/driller/c64.cpp
+++ b/engines/freescape/games/driller/c64.cpp
@@ -163,7 +163,7 @@ void DrillerEngine::loadAssetsC64FullGame() {
 	// the active player's SID is created.
 	_playerC64Sfx = new DrillerC64SFXPlayer();
 	_playerC64Sfx->destroySID();
-	_playerSid = new DrillerSIDPlayer();
+	_playerMusic = new DrillerSIDPlayer();
 
 	// C64 SFX index mapping
 	// Based on analysis of the C64 binary SFX routines
@@ -195,14 +195,12 @@ void DrillerEngine::toggleC64Sound() {
 	if (_c64UseSFX) {
 		if (_playerC64Sfx)
 			_playerC64Sfx->destroySID();
-		if (_playerSid) {
-			_playerSid->initSID();
-			_playerSid->startMusic();
-		}
+		if (_playerMusic)
+			_playerMusic->startMusic();
 		_c64UseSFX = false;
 	} else {
-		if (_playerSid)
-			_playerSid->destroySID();
+		if (_playerMusic)
+			_playerMusic->stopMusic();
 		if (_playerC64Sfx)
 			_playerC64Sfx->initSID();
 		_c64UseSFX = true;
diff --git a/engines/freescape/games/driller/c64.music.cpp b/engines/freescape/games/driller/c64.music.cpp
index e595f713eb8..02c364f2dda 100644
--- a/engines/freescape/games/driller/c64.music.cpp
+++ b/engines/freescape/games/driller/c64.music.cpp
@@ -210,9 +210,10 @@ const uint8_t initialTwoCtr[] = {0x02, 0x02, 0x02};
 // Debug log levels
 #define DEBUG_LEVEL 4 // 0: Minimal, 1: Basic Flow, 2: Detailed State
 
-DrillerSIDPlayer::DrillerSIDPlayer() : _sid(nullptr),
+// Tune 0 seems unused, Tune 1 is the main theme
+DrillerSIDPlayer::DrillerSIDPlayer(int tuneIndex) : _sid(nullptr),
 														  _playState(STOPPED),
-														  _targetTuneIndex(0),
+														  _targetTuneIndex(tuneIndex),
 														  _globalTempo(3),        // Default tempo
 														  _globalTempoCounter(1)  // Start immediately
 {
@@ -234,14 +235,8 @@ void DrillerSIDPlayer::destroySID() {
 	}
 }
 
-// Tune 0 seems unused, Tune 1 is the main theme
-void DrillerSIDPlayer::startMusic(int tuneIndex) {
-	if (tuneIndex < 0 || tuneIndex >= NUM_TUNES) {
-		debug(DEBUG_LEVEL >= 0, "Driller: Invalid tune index %d requested", tuneIndex);
-		return;
-	}
-	debug(DEBUG_LEVEL >= 0, "Driller: Starting Tune %d", tuneIndex);
-	_targetTuneIndex = tuneIndex;
+void DrillerSIDPlayer::startMusic() {
+	initSID();
 	// Signal to change tune on the next frame update
 	// If stopped, this will trigger initialization. If playing, triggers change.
 	_playState = CHANGING_TUNE;
@@ -258,6 +253,11 @@ void DrillerSIDPlayer::stopMusic() {
 			SID_Write(offset + 4, 0); // Gate off, keep waveform bits
 		}
 	}
+	destroySID();
+}
+
+bool DrillerSIDPlayer::isPlaying() const {
+        return _playState == PLAYING;
 }
 
 // --- SID Interaction ---
diff --git a/engines/freescape/games/driller/c64.music.h b/engines/freescape/games/driller/c64.music.h
index f36fc86491f..745e35f0992 100644
--- a/engines/freescape/games/driller/c64.music.h
+++ b/engines/freescape/games/driller/c64.music.h
@@ -21,10 +21,11 @@
 
 #include "audio/sid.h"
 #include "common/debug.h"
+#include "freescape/music.h"
 
 namespace Freescape {
 
-class DrillerSIDPlayer {
+class DrillerSIDPlayer : public MusicPlayer {
 
 	// --- Voice State Structure ---
 	struct VoiceState {
@@ -221,14 +222,15 @@ class DrillerSIDPlayer {
 	// Gate mask is now per-voice (v.gateMask) matching assembly's control3
 
 public:
-	DrillerSIDPlayer();
+	DrillerSIDPlayer(int tuneIndex = 1);
 	~DrillerSIDPlayer();
-	void startMusic(int tuneIndex = 1);
-	void stopMusic();
+	void startMusic() override;
+	void stopMusic() override;
+	bool isPlaying() const override;
+
+private:
 	void initSID();
 	void destroySID();
-
-	private:
 	void SID_Write(int reg, uint8_t data);
 	void onTimer();
 	void handleChangeTune(int tuneIndex);
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index b5c0b04c7dd..65629226fa4 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -98,13 +98,13 @@ DrillerEngine::DrillerEngine(OSystem *syst, const ADGameDescription *gd) : Frees
 
 	_borderExtra = nullptr;
 	_borderExtraTexture = nullptr;
-	_playerSid = nullptr;
+	_playerMusic = nullptr;
 	_playerC64Sfx = nullptr;
 	_c64UseSFX = false;
 }
 
 DrillerEngine::~DrillerEngine() {
-	delete _playerSid;
+	delete _playerMusic;
 	delete _playerC64Sfx;
 	delete _drillBase;
 
@@ -268,8 +268,8 @@ void DrillerEngine::gotoArea(uint16 areaID, int entranceID) {
 
 	if (areaID == _startArea && entranceID == _startEntrance) {
 		if (isC64()) {
-			if (!_c64UseSFX && _playerSid)
-				_playerSid->startMusic();
+			if (!_c64UseSFX && _playerMusic)
+				_playerMusic->startMusic();
 			playSound(_soundIndexStart, true, _soundFxHandle);
 		} else {
 			playSound(_soundIndexStart, true, _soundFxHandle);
diff --git a/engines/freescape/games/driller/driller.h b/engines/freescape/games/driller/driller.h
index e427f1ab3d5..6db575dc442 100644
--- a/engines/freescape/games/driller/driller.h
+++ b/engines/freescape/games/driller/driller.h
@@ -22,6 +22,7 @@
 #include "audio/audiostream.h"
 #include "audio/mixer.h"
 
+#include "engines/freescape/music.h"
 #include "engines/freescape/games/driller/c64.music.h"
 #include "engines/freescape/games/driller/c64.sfx.h"
 
@@ -45,7 +46,7 @@ public:
 
 	bool _useAutomaticDrilling;
 
-	DrillerSIDPlayer *_playerSid;
+	MusicPlayer *_playerMusic;
 	DrillerC64SFXPlayer *_playerC64Sfx;
 	bool _c64UseSFX;
 
diff --git a/engines/freescape/games/eclipse/ay.music.h b/engines/freescape/games/eclipse/ay.music.h
index bf8d61434c5..e679a651b99 100644
--- a/engines/freescape/games/eclipse/ay.music.h
+++ b/engines/freescape/games/eclipse/ay.music.h
@@ -24,6 +24,7 @@
 
 #include "audio/softsynth/ay8912.h"
 #include "audio/mixer.h"
+#include "freescape/music.h"
 
 namespace Freescape {
 
@@ -37,14 +38,14 @@ namespace Freescape {
  * - Dropping SID-specific features (pulse width, filters)
  *
  */
-class EclipseAYMusicPlayer : public Audio::AY8912Stream {
+class EclipseAYMusicPlayer : public MusicPlayer, private Audio::AY8912Stream {
 public:
 	EclipseAYMusicPlayer(Audio::Mixer *mixer);
 	~EclipseAYMusicPlayer() override;
 
-	void startMusic();
-	void stopMusic();
-	bool isPlaying() const;
+	void startMusic() override;
+	void stopMusic() override;
+	bool isPlaying() const override;
 
 	// AudioStream overrides
 	int readBuffer(int16 *buffer, const int numSamples) override;
diff --git a/engines/freescape/games/eclipse/c64.cpp b/engines/freescape/games/eclipse/c64.cpp
index 59e954280fb..657f5434c4f 100644
--- a/engines/freescape/games/eclipse/c64.cpp
+++ b/engines/freescape/games/eclipse/c64.cpp
@@ -116,8 +116,8 @@ void EclipseEngine::loadAssetsC64FullGame() {
 				if (loadAddress == 0x0410) {
 					_c64MusicData.resize(file.size() - 2);
 					file.read(_c64MusicData.data(), _c64MusicData.size());
-					delete _playerC64Music;
-					_playerC64Music = new EclipseC64MusicPlayer(_c64MusicData);
+					delete _playerMusic;
+					_playerMusic = new EclipseC64MusicPlayer(_c64MusicData);
 				}
 			}
 		} else if ((_variant & GF_C64_TAPE) && _extraBuffer) {
@@ -128,8 +128,8 @@ void EclipseEngine::loadAssetsC64FullGame() {
 			static const int kMusicRegionSize = 0x1100; // covers 0x0410..0x14FF
 			_c64MusicData.resize(kMusicRegionSize);
 			memcpy(_c64MusicData.data(), _extraBuffer + kTapeMusicShift, kMusicRegionSize);
-			delete _playerC64Music;
-			_playerC64Music = new EclipseC64MusicPlayer(_c64MusicData);
+			delete _playerMusic;
+			_playerMusic = new EclipseC64MusicPlayer(_c64MusicData);
 		}
 	} else {
 		Common::File musicFile;
@@ -139,8 +139,8 @@ void EclipseEngine::loadAssetsC64FullGame() {
 			if (loadAddress == 0x0410) {
 				_c64MusicData.resize(musicFile.size() - 2);
 				musicFile.read(_c64MusicData.data(), _c64MusicData.size());
-				delete _playerC64Music;
-				_playerC64Music = new EclipseC64MusicPlayer(_c64MusicData);
+				delete _playerMusic;
+				_playerMusic = new EclipseC64MusicPlayer(_c64MusicData);
 			}
 		}
 	}
@@ -162,14 +162,12 @@ void EclipseEngine::toggleC64Sound() {
 	if (_c64UseSFX) {
 		if (_playerC64Sfx)
 			_playerC64Sfx->destroySID();
-		if (_playerC64Music) {
-			_playerC64Music->initSID();
-			_playerC64Music->startMusic();
-		}
+		if (_playerMusic)
+			_playerMusic->startMusic();
 		_c64UseSFX = false;
 	} else {
-		if (_playerC64Music)
-			_playerC64Music->destroySID();
+		if (_playerMusic)
+			_playerMusic->stopMusic();
 		if (_playerC64Sfx)
 			_playerC64Sfx->initSID();
 		_c64UseSFX = true;
diff --git a/engines/freescape/games/eclipse/c64.music.cpp b/engines/freescape/games/eclipse/c64.music.cpp
index 82fae5526ca..09acca9092c 100644
--- a/engines/freescape/games/eclipse/c64.music.cpp
+++ b/engines/freescape/games/eclipse/c64.music.cpp
@@ -150,12 +150,14 @@ byte EclipseC64MusicPlayer::clampNote(byte note) const {
 void EclipseC64MusicPlayer::startMusic() {
 	if (_musicData.empty())
 		return;
+	initSID();
 	setupSong();
 }
 
 void EclipseC64MusicPlayer::stopMusic() {
 	_musicActive = false;
 	silenceAll();
+	destroySID();
 }
 
 void EclipseC64MusicPlayer::setupSong() {
diff --git a/engines/freescape/games/eclipse/c64.music.h b/engines/freescape/games/eclipse/c64.music.h
index 8496dff7f35..cf8b582c8b3 100644
--- a/engines/freescape/games/eclipse/c64.music.h
+++ b/engines/freescape/games/eclipse/c64.music.h
@@ -24,19 +24,18 @@
 
 #include "audio/sid.h"
 #include "common/array.h"
+#include "freescape/music.h"
 
 namespace Freescape {
 
-class EclipseC64MusicPlayer {
+class EclipseC64MusicPlayer : public MusicPlayer {
 public:
 	EclipseC64MusicPlayer(const Common::Array<byte> &musicData);
 	~EclipseC64MusicPlayer();
 
-	void startMusic();
-	void stopMusic();
-	bool isPlaying() const;
-	void initSID();
-	void destroySID();
+	void startMusic() override;
+	void stopMusic() override;
+	bool isPlaying() const override;
 
 private:
 	static const uint16 kLoadAddress = 0x0410;
@@ -128,6 +127,9 @@ private:
 	void applyEffectArpeggio(int channel);
 	void applyTimedSlide(int channel);
 	void applyPulseWidthModulation(int channel);
+
+	void initSID();
+	void destroySID();
 };
 
 } // namespace Freescape
diff --git a/engines/freescape/games/eclipse/cpc.cpp b/engines/freescape/games/eclipse/cpc.cpp
index 48078bd65ff..43480d310b2 100644
--- a/engines/freescape/games/eclipse/cpc.cpp
+++ b/engines/freescape/games/eclipse/cpc.cpp
@@ -146,7 +146,7 @@ void EclipseEngine::loadAssetsCPCFullGame() {
 		it->convertToInPlace(_gfx->_texturePixelFormat);
 
 	if (ConfMan.getBool("ay_music"))
-		_playerAYMusic = new EclipseAYMusicPlayer(_mixer);
+		_playerMusic = new EclipseAYMusicPlayer(_mixer);
 }
 
 void EclipseEngine::loadAssetsCPCDemo() {
@@ -187,7 +187,7 @@ void EclipseEngine::loadAssetsCPCDemo() {
 		it->convertToInPlace(_gfx->_texturePixelFormat);
 
 	if (ConfMan.getBool("ay_music"))
-		_playerAYMusic = new EclipseAYMusicPlayer(_mixer);
+		_playerMusic = new EclipseAYMusicPlayer(_mixer);
 }
 
 void EclipseEngine::updateHeartFramesCPC() {
diff --git a/engines/freescape/games/eclipse/dos.cpp b/engines/freescape/games/eclipse/dos.cpp
index 2019f48a3d9..e0dc3707f7e 100644
--- a/engines/freescape/games/eclipse/dos.cpp
+++ b/engines/freescape/games/eclipse/dos.cpp
@@ -162,7 +162,7 @@ void EclipseEngine::loadAssetsDOSFullGame() {
 		error("Invalid or unsupported render mode %s for Total Eclipse", Common::getRenderModeDescription(_renderMode));
 
 	if (ConfMan.getBool("opl_music"))
-		_playerOPLMusic = new EclipseOPLMusicPlayer();
+		_playerMusic = new EclipseOPLMusicPlayer();
 }
 
 void EclipseEngine::drawDOSUI(Graphics::Surface *surface) {
diff --git a/engines/freescape/games/eclipse/eclipse.cpp b/engines/freescape/games/eclipse/eclipse.cpp
index 23c9c54eb0b..6474c859302 100644
--- a/engines/freescape/games/eclipse/eclipse.cpp
+++ b/engines/freescape/games/eclipse/eclipse.cpp
@@ -58,10 +58,8 @@ static const WBTableOffsets kEclipseAmigaMusicOffsets = {
 };
 
 EclipseEngine::EclipseEngine(OSystem *syst, const ADGameDescription *gd) : FreescapeEngine(syst, gd) {
-	_playerC64Music = nullptr;
 	_playerC64Sfx = nullptr;
-	_playerAYMusic = nullptr;
-	_playerOPLMusic = nullptr;
+	_playerMusic = nullptr;
 	_c64UseSFX = false;
 
 	// These sounds can be overriden by the class of each platform
@@ -136,23 +134,15 @@ EclipseEngine::EclipseEngine(OSystem *syst, const ADGameDescription *gd) : Frees
 }
 
 void EclipseEngine::stopBackgroundMusic() {
-	if (_playerOPLMusic)
-		_playerOPLMusic->stopMusic();
-	if (_playerAYMusic)
-		_playerAYMusic->stopMusic();
-	if (_playerC64Music)
-		_playerC64Music->stopMusic();
+	if (_playerMusic)
+		_playerMusic->stopMusic();
 	if (_mixer)
 		_mixer->stopHandle(_musicHandle);
 }
 
 void EclipseEngine::restartBackgroundMusic() {
-	if (isC64() && _playerC64Music) {
-		_playerC64Music->startMusic();
-	} else if ((isCPC() || isSpectrum()) && _playerAYMusic) {
-		_playerAYMusic->startMusic();
-	} else if (isDOS() && _playerOPLMusic) {
-		_playerOPLMusic->startMusic();
+	if (_playerMusic) {
+		_playerMusic->startMusic();
 	} else if ((isAtariST() || isAmiga()) && !_musicData.empty()) {
 		if (_mixer)
 			_mixer->stopHandle(_musicHandle);
@@ -183,9 +173,7 @@ EclipseEngine::~EclipseEngine() {
 		_compassBackground->free();
 		delete _compassBackground;
 	}
-	delete _playerOPLMusic;
-	delete _playerAYMusic;
-	delete _playerC64Music;
+	delete _playerMusic;
 	delete _playerC64Sfx;
 }
 
diff --git a/engines/freescape/games/eclipse/eclipse.h b/engines/freescape/games/eclipse/eclipse.h
index 2ae3b43b1ba..a36195c3696 100644
--- a/engines/freescape/games/eclipse/eclipse.h
+++ b/engines/freescape/games/eclipse/eclipse.h
@@ -21,14 +21,12 @@
 
 #include "common/file.h"
 
+#include "freescape/music.h"
 #include "freescape/sound.h"
 
 namespace Freescape {
 
-class EclipseAYMusicPlayer;
-class EclipseC64MusicPlayer;
 class EclipseC64SFXPlayer;
-class EclipseOPLMusicPlayer;
 
 enum EclipseReleaseFlags {
 	GF_ZX_DEMO_CRASH = (1 << 0),
@@ -123,14 +121,12 @@ public:
 
 	Common::Array<byte> _musicData; // TEMUSIC.ST TEXT segment (Atari ST)
 	Common::Array<byte> _c64MusicData;
-	EclipseC64MusicPlayer *_playerC64Music;
 	EclipseC64SFXPlayer *_playerC64Sfx;
 	bool _c64UseSFX;
 	void playSoundC64(int index) override;
 	void toggleC64Sound();
 
-	EclipseAYMusicPlayer *_playerAYMusic;
-	EclipseOPLMusicPlayer *_playerOPLMusic;
+	MusicPlayer *_playerMusic;
 	void restartBackgroundMusic();
 	void stopBackgroundMusic();
 
diff --git a/engines/freescape/games/eclipse/opl.music.h b/engines/freescape/games/eclipse/opl.music.h
index 466a898bf7d..90265ef8578 100644
--- a/engines/freescape/games/eclipse/opl.music.h
+++ b/engines/freescape/games/eclipse/opl.music.h
@@ -23,6 +23,7 @@
 #define FREESCAPE_ECLIPSE_OPL_MUSIC_H
 
 #include "audio/fmopl.h"
+#include "freescape/music.h"
 
 namespace Freescape {
 
@@ -35,14 +36,14 @@ namespace Freescape {
  * - Mapping SID waveforms to OPL FM instrument patches
  * - Rebuilding the SID envelope and pulse-width motion on top of AdLib timbres
  */
-class EclipseOPLMusicPlayer {
+class EclipseOPLMusicPlayer : public MusicPlayer {
 public:
 	EclipseOPLMusicPlayer();
 	~EclipseOPLMusicPlayer();
 
-	void startMusic();
-	void stopMusic();
-	bool isPlaying() const;
+	void startMusic() override;
+	void stopMusic() override;
+	bool isPlaying() const override;
 
 private:
 	enum {
diff --git a/engines/freescape/games/eclipse/zx.cpp b/engines/freescape/games/eclipse/zx.cpp
index 2753a4c8823..103e11d3760 100644
--- a/engines/freescape/games/eclipse/zx.cpp
+++ b/engines/freescape/games/eclipse/zx.cpp
@@ -128,7 +128,7 @@ void EclipseEngine::loadAssetsZXFullGame() {
 		it->convertToInPlace(_gfx->_texturePixelFormat);
 
 	if (ConfMan.getBool("ay_music"))
-		_playerAYMusic = new EclipseAYMusicPlayer(_mixer);
+		_playerMusic = new EclipseAYMusicPlayer(_mixer);
 }
 
 void EclipseEngine::loadAssetsZXDemo() {
@@ -173,7 +173,7 @@ void EclipseEngine::loadAssetsZXDemo() {
 		it->convertToInPlace(_gfx->_texturePixelFormat);
 
 	if (ConfMan.getBool("ay_music"))
-		_playerAYMusic = new EclipseAYMusicPlayer(_mixer);
+		_playerMusic = new EclipseAYMusicPlayer(_mixer);
 }
 
 void EclipseEngine::drawZXUI(Graphics::Surface *surface) {
diff --git a/engines/freescape/music.h b/engines/freescape/music.h
new file mode 100644
index 00000000000..8b968078956
--- /dev/null
+++ b/engines/freescape/music.h
@@ -0,0 +1,40 @@
+/* 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_MUSIC_H
+#define FREESCAPE_MUSIC_H
+
+#include "common/scummsys.h"
+
+namespace Freescape {
+
+class MusicPlayer {
+public:
+	virtual ~MusicPlayer() {}
+
+	virtual void startMusic() = 0;
+	virtual void stopMusic() = 0;
+	virtual bool isPlaying() const = 0;
+};
+
+} // namespace Freescape
+
+#endif


Commit: a295fa5a0b7d6b46584d71b2a746d3b5536dc692
    https://github.com/scummvm/scummvm/commit/a295fa5a0b7d6b46584d71b2a746d3b5536dc692
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-05-04T23:34:51+02:00

Commit Message:
FREESCAPE: Refactor Atari music code to be independent of the YM2149 implementation

Changed paths:
    engines/freescape/games/eclipse/atari.cpp
    engines/freescape/games/eclipse/atari.music.cpp
    engines/freescape/games/eclipse/eclipse.cpp


diff --git a/engines/freescape/games/eclipse/atari.cpp b/engines/freescape/games/eclipse/atari.cpp
index 0b33e3668fc..d2d97733d58 100644
--- a/engines/freescape/games/eclipse/atari.cpp
+++ b/engines/freescape/games/eclipse/atari.cpp
@@ -29,6 +29,11 @@
 
 namespace Freescape {
 
+
+// Forward declaration (defined in atari.music.cpp)
+MusicPlayer *makeEclipseAtariMusicPlayer(const byte *data, uint32 dataSize,
+                                                  int songNum = 1);
+
 extern const int kAtariCompassPhaseCount = 72;
 extern const int kAtariCompassBaseFrames = 19;
 extern const int kAtariCompassTotalFrames = 37;
@@ -850,6 +855,7 @@ void EclipseEngine::loadAssetsAtariFullGame() {
 	stream->seek(kTEMusicOffset + kGemdosHeaderSize);
 	_musicData.resize(kTEMusicTextSize);
 	stream->read(_musicData.data(), kTEMusicTextSize);
+	_playerMusic = makeEclipseAtariMusicPlayer(_musicData.data(), _musicData.size());
 	debug(3, "TE-Atari: Loaded TEMUSIC.ST TEXT segment (%d bytes)", kTEMusicTextSize);
 
 	// UI font (Font A): 4-plane 16-color bordered font at prog $24C3E (file offset $24C5A)
diff --git a/engines/freescape/games/eclipse/atari.music.cpp b/engines/freescape/games/eclipse/atari.music.cpp
index 29a056f5991..ca6d0dee517 100644
--- a/engines/freescape/games/eclipse/atari.music.cpp
+++ b/engines/freescape/games/eclipse/atari.music.cpp
@@ -35,9 +35,10 @@
  *   $0DCC  Pattern pointer table (up to 31 x uint32 BE)
  */
 
-#include "audio/softsynth/ym2149.h"
+#include "audio/ym2149.h"
 
 #include "freescape/freescape.h"
+#include "freescape/music.h"
 #include "freescape/wb.h"
 
 #include "common/endian.h"
@@ -58,17 +59,18 @@ static const int kTENumPeriods     = 96;
 static const int kTENumInstruments = 12;
 static const int kTEMaxPatterns    = 31;
 
-class EclipseAtariMusicStream : public Audio::YM2149Emu {
+class EclipseAtariMusicPlayer : public MusicPlayer {
 public:
-	EclipseAtariMusicStream(const byte *data, uint32 dataSize, int songNum, int rate = 44100);
-	~EclipseAtariMusicStream() override {}
+	EclipseAtariMusicPlayer(const byte *data, uint32 dataSize, int songNum);
+	~EclipseAtariMusicPlayer();
 
-	int readBuffer(int16 *buffer, const int numSamples) override;
-	bool endOfData() const override { return !_musicActive; }
-	bool endOfStream() const override { return !_musicActive; }
-	Audio::AudioStream *toAudioStream() { return this; }
+	void startMusic() override;
+	void stopMusic() override;
+	bool isPlaying() const override;
 
 private:
+	YM2149::YM2149 *_ym2149;
+
 	// --- Data tables ---
 	const byte *_data;
 	uint32 _dataSize;
@@ -178,10 +180,10 @@ private:
 	bool _musicActive;
 	byte _tickSpeed;
 	byte _tickCounter;
-	int _tickSampleCount;
 	bool _hwEnvelopeDirty;
 	uint16 _hwEnvelopePeriod;
 	byte _hwEnvelopeShape;
+	int _songNum;
 
 	// --- Methods ---
 	void loadTables();
@@ -195,7 +197,7 @@ private:
 	void buildArpeggioTable(ChannelState &c, byte mask);
 	void tickUpdate();
 	void writeYMRegisters();
-	void setReg(int reg, byte value) { writeReg(reg, value); }
+	void setReg(int reg, byte value) { if (_ym2149) _ym2149->writeReg(reg, value); }
 
 	uint16 getPeriod(int note) const {
 		if (note < 0 || note >= kTENumPeriods)
@@ -226,12 +228,12 @@ private:
 // Construction / data loading
 // ---------------------------------------------------------------------------
 
-EclipseAtariMusicStream::EclipseAtariMusicStream(const byte *data, uint32 dataSize,
-                                                   int songNum, int)
+EclipseAtariMusicPlayer::EclipseAtariMusicPlayer(const byte *data, uint32 dataSize,
+                                                   int songNum)
 	: _data(data), _dataSize(dataSize),
 	  _musicActive(false), _tickSpeed(6), _tickCounter(0),
-	  _tickSampleCount(0), _hwEnvelopeDirty(false), _hwEnvelopePeriod(0), _hwEnvelopeShape(0),
-	  _numPatterns(0) {
+	  _hwEnvelopeDirty(false), _hwEnvelopePeriod(0), _hwEnvelopeShape(0),
+	  _numPatterns(0), _songNum(songNum) {
 
 	memset(_periods, 0, sizeof(_periods));
 	memset(_instruments, 0, sizeof(_instruments));
@@ -240,13 +242,47 @@ EclipseAtariMusicStream::EclipseAtariMusicStream(const byte *data, uint32 dataSi
 	memset(_arpeggioIntervals, 0, sizeof(_arpeggioIntervals));
 	memset(_channels, 0, sizeof(_channels));
 
-	init();
+	_ym2149 = YM2149::Config::create();
+	if (!_ym2149 || !_ym2149->init()) {
+		warning("EclipseAtariMusicPlayer: Failed to create YM2149 emulator");
+		delete _ym2149;
+		_ym2149 = nullptr;
+	}
 
 	loadTables();
-	startSong(songNum);
 }
 
-void EclipseAtariMusicStream::loadTables() {
+EclipseAtariMusicPlayer::~EclipseAtariMusicPlayer() {
+	stopMusic();
+	delete _ym2149;
+}
+
+
+// ============================================================================
+// Public interface
+// ============================================================================
+
+void EclipseAtariMusicPlayer::startMusic() {
+	if (!_ym2149)
+		return;
+	stopMusic();
+	_ym2149->start(new Common::Functor0Mem<void, EclipseAtariMusicPlayer>(
+		this, &EclipseAtariMusicPlayer::tickUpdate), 50);
+	startSong(_songNum);
+}
+
+void EclipseAtariMusicPlayer::stopMusic() {
+	_musicActive = false;
+	if (_ym2149) {
+		_ym2149->stop();
+	}
+}
+
+bool EclipseAtariMusicPlayer::isPlaying() const {
+	return _musicActive;
+}
+
+void EclipseAtariMusicPlayer::loadTables() {
 	// Period table: 96 x uint16 BE at TEXT+$0B24
 	for (int i = 0; i < kTENumPeriods; i++) {
 		_periods[i] = readDataWord(kTEPeriodTableOffset + i * 2);
@@ -314,7 +350,7 @@ void EclipseAtariMusicStream::loadTables() {
 // Song init
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::startSong(int songNum) {
+void EclipseAtariMusicPlayer::startSong(int songNum) {
 	_musicActive = false;
 
 	if (songNum < 1 || songNum > 2)
@@ -349,7 +385,7 @@ void EclipseAtariMusicStream::startSong(int songNum) {
 	}
 }
 
-void EclipseAtariMusicStream::initChannel(int ch) {
+void EclipseAtariMusicPlayer::initChannel(int ch) {
 	ChannelState &c = _channels[ch];
 	memset(&c, 0, sizeof(ChannelState));
 	c.duration = 1;
@@ -366,7 +402,7 @@ void EclipseAtariMusicStream::initChannel(int ch) {
 // Same format as wb.cpp: $00-$C0=pattern#, $C1-$FE=transpose, $FF=loop
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::readOrderList(int ch) {
+void EclipseAtariMusicPlayer::readOrderList(int ch) {
 	ChannelState &c = _channels[ch];
 
 	for (int safety = 0; safety < 256; safety++) {
@@ -408,7 +444,7 @@ void EclipseAtariMusicStream::readOrderList(int ch) {
 //   $7D/$7C=vibrato/arpeggio, $00-$5F=note
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::readPatternCommands(int ch) {
+void EclipseAtariMusicPlayer::readPatternCommands(int ch) {
 	ChannelState &c = _channels[ch];
 
 	for (int safety = 0; safety < 256; safety++) {
@@ -605,7 +641,7 @@ void EclipseAtariMusicStream::readPatternCommands(int ch) {
 // Note trigger — set YM period, reset envelope
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::triggerNote(int ch) {
+void EclipseAtariMusicPlayer::triggerNote(int ch) {
 	ChannelState &c = _channels[ch];
 
 	// Apply transpose and clamp
@@ -707,7 +743,7 @@ void EclipseAtariMusicStream::triggerNote(int ch) {
 // Effects processing — runs every tick (50 Hz)
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::processEffects(int ch) {
+void EclipseAtariMusicPlayer::processEffects(int ch) {
 	ChannelState &c = _channels[ch];
 
 	// Noise gate: in TEMUSIC, instrument high nibble is a countdown that
@@ -823,7 +859,7 @@ void EclipseAtariMusicStream::processEffects(int ch) {
 // Volume range: 0-63 internal, written to YM as >>2 (0-15)
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::processEnvelope(int ch) {
+void EclipseAtariMusicPlayer::processEnvelope(int ch) {
 	ChannelState &c = _channels[ch];
 	// Noise-only instruments may validly run with zero tone period.
 	if (c.outputPeriod == 0 && !c.noiseEnabled)
@@ -900,7 +936,7 @@ void EclipseAtariMusicStream::processEnvelope(int ch) {
 // Arpeggio table builder
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::buildArpeggioTable(ChannelState &c, byte mask) {
+void EclipseAtariMusicPlayer::buildArpeggioTable(ChannelState &c, byte mask) {
 	c.arpeggioTableLen = WBCommon::buildArpeggioTable(_arpeggioIntervals, mask, c.arpeggioTable, 16, false);
 	c.arpeggioPos = 0;
 }
@@ -909,7 +945,7 @@ void EclipseAtariMusicStream::buildArpeggioTable(ChannelState &c, byte mask) {
 // Write channel state to YM2149 registers
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::writeYMRegisters() {
+void EclipseAtariMusicPlayer::writeYMRegisters() {
 	byte mixer = 0x3F; // Start with all disabled (bits 0-2=tone, bits 3-5=noise)
 	if (_hwEnvelopeDirty) {
 		setReg(11, _hwEnvelopePeriod & 0xFF);
@@ -970,7 +1006,7 @@ void EclipseAtariMusicStream::writeYMRegisters() {
 // Main tick update — called at 50 Hz
 // ---------------------------------------------------------------------------
 
-void EclipseAtariMusicStream::tickUpdate() {
+void EclipseAtariMusicPlayer::tickUpdate() {
 	if (!_musicActive)
 		return;
 
@@ -1023,49 +1059,18 @@ void EclipseAtariMusicStream::tickUpdate() {
 	writeYMRegisters();
 }
 
-// ---------------------------------------------------------------------------
-// Audio stream readBuffer — tick at 50 Hz, generate AY samples
-// ---------------------------------------------------------------------------
-
-int EclipseAtariMusicStream::readBuffer(int16 *buffer, const int numSamples) {
-	if (!_musicActive)
-		return 0;
-
-	int samplesGenerated = 0;
-	int samplesPerTick = MAX(1, getRate() / 50);
-
-	while (samplesGenerated < numSamples && _musicActive) {
-		int remaining = samplesPerTick - _tickSampleCount;
-		int toGenerate = MIN(numSamples - samplesGenerated, remaining);
-
-		if (toGenerate > 0) {
-			generateSamples(buffer + samplesGenerated, toGenerate);
-			samplesGenerated += toGenerate;
-			_tickSampleCount += toGenerate;
-		}
-
-		if (_tickSampleCount >= samplesPerTick) {
-			_tickSampleCount -= samplesPerTick;
-			tickUpdate();
-		}
-	}
-
-	return samplesGenerated;
-}
-
 // ---------------------------------------------------------------------------
 // Factory function
 // ---------------------------------------------------------------------------
 
-Audio::AudioStream *makeEclipseAtariMusicStream(const byte *data, uint32 dataSize,
-                                                  int songNum, int rate) {
+MusicPlayer *makeEclipseAtariMusicPlayer(const byte *data, uint32 dataSize,
+                                                  int songNum) {
 	if (!data || dataSize < 0x1000) {
 		warning("TE-Atari music: invalid data (size %u)", dataSize);
 		return nullptr;
 	}
 
-	EclipseAtariMusicStream *stream = new EclipseAtariMusicStream(data, dataSize, songNum, rate);
-	return stream->toAudioStream();
+	return new EclipseAtariMusicPlayer(data, dataSize, songNum);
 }
 
 } // End of namespace Freescape
diff --git a/engines/freescape/games/eclipse/eclipse.cpp b/engines/freescape/games/eclipse/eclipse.cpp
index 6474c859302..3262c69883b 100644
--- a/engines/freescape/games/eclipse/eclipse.cpp
+++ b/engines/freescape/games/eclipse/eclipse.cpp
@@ -41,10 +41,6 @@
 
 namespace Freescape {
 
-// Forward declaration (defined in atari.music.cpp)
-Audio::AudioStream *makeEclipseAtariMusicStream(const byte *data, uint32 dataSize,
-                                                  int songNum = 1, int rate = 44100);
-
 // Wally Beben table offsets for Total Eclipse Amiga TEMUSIC.AM
 static const WBTableOffsets kEclipseAmigaMusicOffsets = {
 	0x0ACA, // periodTable
@@ -143,17 +139,12 @@ void EclipseEngine::stopBackgroundMusic() {
 void EclipseEngine::restartBackgroundMusic() {
 	if (_playerMusic) {
 		_playerMusic->startMusic();
-	} else if ((isAtariST() || isAmiga()) && !_musicData.empty()) {
+	} else if (isAmiga() && !_musicData.empty()) {
 		if (_mixer)
 			_mixer->stopHandle(_musicHandle);
-		Audio::AudioStream *musicStream = nullptr;
-		if (isAmiga())
-			musicStream = makeWallyBebenStream(
-				_musicData.data(), _musicData.size(), 1, 44100, true,
-				&kEclipseAmigaMusicOffsets);
-		else
-			musicStream = makeEclipseAtariMusicStream(
-				_musicData.data(), _musicData.size(), 1);
+		Audio::AudioStream *musicStream = makeWallyBebenStream(
+			_musicData.data(), _musicData.size(), 1, 44100, true,
+			&kEclipseAmigaMusicOffsets);
 		if (musicStream) {
 			_mixer->playStream(Audio::Mixer::kMusicSoundType,
 				&_musicHandle, musicStream);
@@ -473,16 +464,11 @@ void EclipseEngine::gotoArea(uint16 areaID, int entranceID) {
 		}
 	}
 
-	// Start background music (Atari ST / Amiga)
-	if ((isAtariST() || isAmiga()) && !_musicData.empty() && !_mixer->isSoundHandleActive(_musicHandle)) {
-		Audio::AudioStream *musicStream = nullptr;
-		if (isAmiga())
-			musicStream = makeWallyBebenStream(
-				_musicData.data(), _musicData.size(), 1, 44100, true,
-				&kEclipseAmigaMusicOffsets);
-		else
-			musicStream = makeEclipseAtariMusicStream(
-				_musicData.data(), _musicData.size(), 1);
+	// Start background music (Amiga)
+	if (isAmiga() && !_musicData.empty() && !_mixer->isSoundHandleActive(_musicHandle)) {
+		Audio::AudioStream *musicStream = makeWallyBebenStream(
+			_musicData.data(), _musicData.size(), 1, 44100, true,
+			&kEclipseAmigaMusicOffsets);
 		if (musicStream) {
 			_mixer->playStream(Audio::Mixer::kMusicSoundType,
 				&_musicHandle, musicStream);




More information about the Scummvm-git-logs mailing list