[Scummvm-git-logs] scummvm master -> 35eeb0e0741e40d655f072cefd6834ccabbb2c3d

NMIError noreply at scummvm.org
Fri Jul 3 12:38:41 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:
75d9011fcb AUDIO/MIDI: Add partial support for early HMI format to HMI/HMP MIDI parser
35eeb0e074 MADS: Add MIDI playback code


Commit: 75d9011fcb26727b54325dd58cb5dd300702eed5
    https://github.com/scummvm/scummvm/commit/75d9011fcb26727b54325dd58cb5dd300702eed5
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-07-03T14:10:30+02:00

Commit Message:
AUDIO/MIDI: Add partial support for early HMI format to HMI/HMP MIDI parser

Changed paths:
    audio/midiparser_hmp.cpp
    audio/midiparser_hmp.h


diff --git a/audio/midiparser_hmp.cpp b/audio/midiparser_hmp.cpp
index 66fbe08ab98..101007cb877 100644
--- a/audio/midiparser_hmp.cpp
+++ b/audio/midiparser_hmp.cpp
@@ -24,12 +24,13 @@
 #include "common/algorithm.h"
 #include "common/textconsole.h"
 
-const char MidiParser_HMP::HMP_HEADER[] = "HMIMIDIP";
-const char MidiParser_HMP::HMP_HEADER_VERSION_1[] = "\x00\x00\x00\x00\x00\x00";
-const char MidiParser_HMP::HMP_HEADER_VERSION_013195[] = "013195";
+const char MidiParser_HMP::HEADER_HMI[] = "HMIMIDIR";
+const char MidiParser_HMP::HEADER_HMP[] = "HMIMIDIP";
+const char MidiParser_HMP::HEADER_HMP_VERSION_1[] = "\x00\x00\x00\x00\x00\x00";
+const char MidiParser_HMP::HEADER_HMP_VERSION_013195[] = "013195";
 
 MidiParser_HMP::MidiParser_HMP(int8 source) : MidiParser_SMF(source) {
-	_version = HmpVersion::VERSION_1;
+	_version = HmpVersion::VERSION_HMP_1;
 	_branchOffset = 0;
 	_songLength = 0;
 	memset(_channelPriorities, 0, sizeof(_channelPriorities));
@@ -57,62 +58,60 @@ bool MidiParser_HMP::loadMusic(const byte *data, uint32 size) {
 	const byte *pos = data;
 
 	// Process header
-	if (memcmp(pos, HMP_HEADER, 8)) {
-		warning("Could not find HMIMIDIP header in HMP data");
+	if (memcmp(pos, HEADER_HMI, 7)) {
+		warning("Could not find HMIMIDI header in HMI/HMP data");
 		return false;
 	}
-	pos += 8;
 	_version = determineVersion(pos);
-	// Skip version and padding bytes
-	pos += 24;
+	bool isHmi = _version == HmpVersion::VERSION_HMI;
+
+	// Skip signature, version, length and padding bytes
+	pos += isHmi ? 26 : 32;
+
+	if (isHmi) {
+		_branchOffset = 0;
+	} else {
+		_branchOffset = readWord(pos, _version);
+		// Skip 3 reserved dwords
+		pos += 12;
+	}
 
-	_branchOffset = READ_LE_UINT32(pos);
-	// Skip 3 reserved dwords
-	pos += 16;
 	_numTracks = 1;
-	_numSubtracks[0] = READ_LE_UINT32(pos);
-	pos += 4;
+	_numSubtracks[0] = readWord(pos, _version);
 	// Doesn't seem like this field is actually used...
-	//uint32 ppqn = READ_LE_UINT32(pos);
+	//uint32 ppqn = readWord(pos, _version);
 	_ppqn = 60;
-	pos += 4;
-	uint32 bpm = READ_LE_UINT32(pos);
+	pos += isHmi ? 2 : 4;
+	uint32 bpm = readWord(pos, _version);
 	setTempo(60000000 / bpm);
-	pos += 4;
-	_songLength = READ_LE_UINT32(pos);
-	pos += 4;
+	_songLength = readWord(pos, _version);
 
 	for (int i = 0; i < 16; i++) {
-		_channelPriorities[i] = READ_LE_UINT32(pos);
-		pos += 4;
+		_channelPriorities[i] = readWord(pos, _version);
 	}
 	for (int i = 0; i < 5; i++) {
 		for (int j = 0; j < 32; j++) {
-			_deviceTrackMappings[j][i] = READ_LE_UINT32(pos);
-			pos += 4;
+			_deviceTrackMappings[j][i] = readWord(pos, _version);
 		}
 	}
-	if (_version == HmpVersion::VERSION_013195) {
+	if (_version == HmpVersion::VERSION_HMP_013195) {
 		Common::copy(pos, pos + 128, _restoreControllers);
 		pos += 128;
 	}
 
-	_callbackPointer = READ_LE_UINT32(pos);
-	pos += 4;
-	_callbackSegment = READ_LE_UINT32(pos);
-	pos += 4;
+	_callbackPointer = readWord(pos, _version);
+	_callbackSegment = readWord(pos, _version);
 
 	// Read the tracks
 	for (uint currTrack = 0; currTrack < _numSubtracks[0]; currTrack++) {
-		//uint32 chunkNumber = READ_LE_UINT32(pos);
-		pos += 4;
-		uint32 chunkSize = READ_LE_UINT32(pos);
-		pos += 4;
-		//uint32 trackNumber = READ_LE_UINT32(pos);
-		pos += 4;
+		//uint32 chunkNumber = readWord(pos, _version);
+		pos += isHmi ? 2 : 4;
+		uint32 chunkSize = readWord(pos, _version);
+		//uint32 trackNumber = readWord(pos, _version);
+		pos += isHmi ? 2 : 4;
 
 		_tracks[0][currTrack] = pos;
-		pos += chunkSize - 12;
+		pos += (chunkSize - (isHmi ? 6 : 12));
 	}
 
 	// TODO Read branching data
@@ -124,27 +123,42 @@ int32 MidiParser_HMP::determineDataSize(Common::SeekableReadStream *stream) {
 	int64 startPos = stream->pos();
 
 	// Process header
-	if (strcmp(stream->readString('\x00', 8).c_str(), HMP_HEADER)) {
+	byte signatureBytes[14] = {0};
+	stream->readMultipleLE(*signatureBytes);
+	if (memcmp(signatureBytes, HEADER_HMI, 7)) {
 		return -1;
 	}
-	byte versionBytes[6] = {0};
-	stream->readMultipleLE(*versionBytes);
-	HmpVersion version = determineVersion(versionBytes);
-	stream->skip(18);
-
-	// TODO Figure out size of branching data
-	//uint32 branchOffset = stream->readUint32LE();
-	stream->skip(4);
-	stream->skip(12);
+	HmpVersion version = determineVersion(signatureBytes);
+	bool isHmi = version == HmpVersion::VERSION_HMI;
+	if (isHmi) {
+		stream->skip(12);
+	} else {
+		stream->skip(18);
+		// TODO Figure out size of branching data
+		//uint32 branchOffset = stream->readUint32LE();
+		stream->skip(4);
+		stream->skip(12);
+	}
 
-	uint32 numTracks = stream->readUint32LE();
-	stream->skip(version == HmpVersion::VERSION_013195 ? 852 : 724);
+	uint32 numTracks = readWord(stream, version);
+	// Skip over the rest of the header
+	switch (version) {
+		case HmpVersion::VERSION_HMI:
+			stream->skip(362);
+			break;
+		case HmpVersion::VERSION_HMP_1:
+			stream->skip(724);
+			break;
+		case HmpVersion::VERSION_HMP_013195:
+			stream->skip(852);
+			break;
+	}
 
 	// Read tracks
 	for (uint currTrack = 0; currTrack < numTracks; currTrack++) {
-		stream->skip(4);
-		uint32 chunkSize = stream->readUint32LE();
-		stream->skip(chunkSize - 8);
+		stream->skip(isHmi ? 2 : 4);
+		uint32 chunkSize = readWord(stream, version);
+		stream->skip(chunkSize - (isHmi ? 4 : 8));
 	}
 
 	// stream should now be at the end of the HMP data
@@ -153,14 +167,40 @@ int32 MidiParser_HMP::determineDataSize(Common::SeekableReadStream *stream) {
 }
 
 MidiParser_HMP::HmpVersion MidiParser_HMP::determineVersion(const byte *pos) {
-	if (!memcmp(pos, HMP_HEADER_VERSION_1, 6)) {
-		return HmpVersion::VERSION_1;
-	} else if (!memcmp(pos, HMP_HEADER_VERSION_013195, 6)) {
-		return HmpVersion::VERSION_013195;
+	if (!memcmp(pos, HEADER_HMI, 8)) {
+		return HmpVersion::VERSION_HMI;
+	} else if (memcmp(pos, HEADER_HMP, 8)) {
+		warning("Unknown HMI/HMP signature '%c%c%c%c%c%c%c%c' - assuming HMP version 1", pos[0], pos[1], pos[2], pos[3], pos[4], pos[5], pos[6], pos[7]);
+		return HmpVersion::VERSION_HMP_1;
+	}
+	pos += 8;
+
+	if (!memcmp(pos, HEADER_HMP_VERSION_1, 6)) {
+		return HmpVersion::VERSION_HMP_1;
+	} else if (!memcmp(pos, HEADER_HMP_VERSION_013195, 6)) {
+		return HmpVersion::VERSION_HMP_013195;
 	} else {
 		warning("Unknown HMP version '%c%c%c%c%c%c' - assuming version 1", pos[0], pos[1], pos[2], pos[3], pos[4], pos[5]);
-		return HmpVersion::VERSION_1;
+		return HmpVersion::VERSION_HMP_1;
 	}
 }
 
+uint32 MidiParser_HMP::readWord(const byte*& data, MidiParser_HMP::HmpVersion version) {
+	uint32 result;
+	if (version == HmpVersion::VERSION_HMI) {
+		result = READ_LE_UINT16(data);
+		data += 2;
+	} else {
+		result = READ_LE_UINT32(data);
+		data += 4;
+	}
+	return result;
+}
+
+uint32 MidiParser_HMP::readWord(Common::SeekableReadStream *stream, MidiParser_HMP::HmpVersion version) {
+	if (version == HmpVersion::VERSION_HMI)
+		return stream->readUint16LE();
+	return stream->readUint32LE();
+}
+
 MidiParser *MidiParser::createParser_HMP(int8 source) { return new MidiParser_HMP(source); }
diff --git a/audio/midiparser_hmp.h b/audio/midiparser_hmp.h
index bc2e863d166..00de55d2bf0 100644
--- a/audio/midiparser_hmp.h
+++ b/audio/midiparser_hmp.h
@@ -25,21 +25,23 @@
 #include "audio/midiparser_smf.h"
 
 /**
- * MIDI parser for the HMI SOS format HMP.
+ * MIDI parser for the HMI SOS formats HMI and HMP.
  * 
  * Implementation is incomplete. Currently only the track chunks are read and
- * played back. Not implented yet: device track mapping, branching, callbacks, etc.
+ * played back. Not implemented yet: device track mapping, branching, callbacks, etc.
  */
 class MidiParser_HMP : public MidiParser_SMF {
 protected:
 	enum class HmpVersion {
-		VERSION_1,
-		VERSION_013195
+		VERSION_HMI,
+		VERSION_HMP_1,
+		VERSION_HMP_013195
 	};
 
-	static const char HMP_HEADER[];
-	static const char HMP_HEADER_VERSION_1[];
-	static const char HMP_HEADER_VERSION_013195[];
+	static const char HEADER_HMI[];
+	static const char HEADER_HMP[];
+	static const char HEADER_HMP_VERSION_1[];
+	static const char HEADER_HMP_VERSION_013195[];
 
 	uint32 readDelta(const byte *&data) override;
 
@@ -62,6 +64,11 @@ protected:
 	uint8 _restoreControllers[128];
 	uint32 _callbackPointer;
 	uint32 _callbackSegment;
+
+	// Read a word from the data with a length that is correct for the specified
+	// version (i.e. 2 bytes for HMI, 4 bytes for HMP)
+	uint32 readWord(const byte *&data, HmpVersion version);
+	uint32 readWord(Common::SeekableReadStream *stream, HmpVersion version);
 };
 
 #endif


Commit: 35eeb0e0741e40d655f072cefd6834ccabbb2c3d
    https://github.com/scummvm/scummvm/commit/35eeb0e0741e40d655f072cefd6834ccabbb2c3d
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-07-03T14:10:30+02:00

Commit Message:
MADS: Add MIDI playback code

Changed paths:
    engines/mads/madsv2/forest/forest.cpp
    engines/mads/madsv2/forest/forest.h
    engines/mads/madsv2/forest/midi.cpp
    engines/mads/madsv2/forest/midi.h


diff --git a/engines/mads/madsv2/forest/forest.cpp b/engines/mads/madsv2/forest/forest.cpp
index 27b5c0c432b..3b46ba63ad7 100644
--- a/engines/mads/madsv2/forest/forest.cpp
+++ b/engines/mads/madsv2/forest/forest.cpp
@@ -85,12 +85,26 @@ Common::Error ForestEngine::run() {
 			SearchMan.add("mpslabs", arch);
 	}
 
+	_midiPlayer.open();
+
 	// Run the game
 	Forest::forest_main();
 
 	return Common::kNoError;
 }
 
+void ForestEngine::syncSoundSettings() {
+	Engine::syncSoundSettings();
+
+	_midiPlayer.syncSoundSettings();
+}
+
+void ForestEngine::pauseEngineIntern(bool pause) {
+	Engine::pauseEngineIntern(pause);
+
+	_midiPlayer.pause(pause);
+}
+
 void ForestEngine::global_init_code() {
 	Common::fill(global, global + GLOBAL_LIST_SIZE, 0);
 	Common::fill(flags, flags + 40, 0);
diff --git a/engines/mads/madsv2/forest/forest.h b/engines/mads/madsv2/forest/forest.h
index d73e0f2ebe6..2b873338557 100644
--- a/engines/mads/madsv2/forest/forest.h
+++ b/engines/mads/madsv2/forest/forest.h
@@ -40,6 +40,8 @@ public:
 	~ForestEngine() override;
 
 	Common::Error run() override;
+	void syncSoundSettings() override;
+	void pauseEngineIntern(bool pause) override;
 	bool canLoadGameStateCurrently(Common::U32String *msg) override;
 	void syncRoom(Common::Serializer &s) override;
 
diff --git a/engines/mads/madsv2/forest/midi.cpp b/engines/mads/madsv2/forest/midi.cpp
index 27a15184c8f..53ad9657d00 100644
--- a/engines/mads/madsv2/forest/midi.cpp
+++ b/engines/mads/madsv2/forest/midi.cpp
@@ -19,8 +19,11 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/textconsole.h"
-#include "audio/midiparser_smf.h"
+#include "audio/adlib_hmisos.h"
+#include "audio/midiparser_hmp.h"
+#include "audio/mt32gm.h"
 #include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/env.h"
 #include "mads/madsv2/forest/forest.h"
@@ -31,42 +34,184 @@ namespace Forest {
 
 bool midi_playing;
 
-void MidiPlayer::send(uint32 b) {
-	//_driver->send(b);
-	Audio::MidiPlayer::send(b);
+MidiPlayer::MidiPlayer() {
+	_driver = nullptr;
+	_deviceType = MT_NULL;
+	_parser = nullptr;
+	_data = nullptr;
+	_paused = false;
 }
 
-void MidiPlayer::play(const char *name) {
-	// Open up the MIDI file
-	Common::SeekableReadStream *f = env_open(name);
-	if (!f) {
-		warning("MIDI not found - %s", name);
-		return;
+MidiPlayer::~MidiPlayer() {
+	stop();
+
+	if (_driver) {
+		_driver->setTimerCallback(nullptr, nullptr);
+		_driver->close();
+	}
+
+	Common::StackLock lock(_mutex);
+
+	if (_parser) {
+		delete _parser;
+		_parser = nullptr;
+	}
+	if (_data) {
+		delete[] _data;
+		_data = nullptr;
 	}
+	if (_driver) {
+		delete _driver;
+		_driver = nullptr;
+	}
+}
 
-#if 0
-	// Read in the file contents
-	size_t size = f->size();
-	byte *buf = (byte *)malloc(size);
-	f->read(buf, size);
-	delete f;
+int MidiPlayer::open() {
+	// Don't call open() twice!
+	assert(!_driver);
 
-	// Set up the parser
-	_parser = new MidiParser_SMF();
-	if (!_parser->loadMusic(buf, size))
-		error("midiPlay() couldn't load music resource");
+	// Check the type of device that the user has configured.
+	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM);
+	_deviceType = MidiDriver::getMusicType(dev);
+	if (_deviceType == MT_GM && ConfMan.getBool("native_mt32"))
+		_deviceType = MT_MT32;
 
-	_parser->setTrack(0);
-	_parser->setMidiDriver(this);
+	OPL::Config::OplType oplType;
+	switch (_deviceType) {
+	case MT_ADLIB:
+		oplType = MidiDriver_ADLIB_Multisource::detectOplType(OPL::Config::kOpl3) ? OPL::Config::kOpl3 : OPL::Config::kOpl2;
+		MidiDriver_ADLIB_HMISOS *adLibDriver;
+		adLibDriver = new MidiDriver_ADLIB_HMISOS(oplType);
+		_driver = adLibDriver;
+
+		Common::SeekableReadStream *instrumentBankStream;
+		instrumentBankStream = SearchMan.createReadStreamForMember(Common::Path("MELODIC.BNK"));
+		Common::SeekableReadStream *rhythmBankStream;
+		rhythmBankStream = SearchMan.createReadStreamForMember(Common::Path("DRUM.BNK"));
+
+		adLibDriver->loadInstrumentBanks(instrumentBankStream, rhythmBankStream);
+
+		break;
+	case MT_GM:
+	case MT_MT32:
+		_driver = new MidiDriver_MT32GM(MusicType::MT_GM);
+		break;
+	default:
+		_driver = new MidiDriver_NULL_Multisource();
+		break;
+	}
+
+	_parser = new MidiParser_HMP(0);
+	_driver->property(MidiDriver::PROP_USER_VOLUME_SCALING, true);
+	_parser->property(MidiParser::mpDisableAutoStartPlayback, true);
+
+	// Open the MIDI driver.
+	const int returnCode = _driver->open();
+	if (returnCode != 0)
+		error("MidiPlayer::open - Failed to open MIDI music driver - error code %d.", returnCode);
+
+	syncSoundSettings();
+
+	// Connect the driver and the parser.
+	_parser->setMidiDriver(_driver);
 	_parser->setTimerRate(_driver->getBaseTempo());
-#else
-	warning("TODO: Support hmi midi playback");
-	delete f;
-#endif
+	_driver->setTimerCallback(this, &onTimer);
+
+	return 0;
+}
+
+void MidiPlayer::load(Common::SeekableReadStream *in, int64 size) {
+	Common::StackLock lock(_mutex);
+
+	if (size < 0) {
+		// Use the parser to determine the size of the MIDI data.
+		int64 startPos = in->pos();
+		size = _parser->determineDataSize(in);
+		if (size < 0) {
+			warning("MidiPlayer::load - Could not determine size of music data");
+			return;
+		}
+		// determineDataSize might move the stream position, so return it to
+		// the original position.
+		in->seek(startPos);
+	}
+
+	_parser->unloadMusic();
+	delete[] _data;
+
+	// Copy the music data
+	_data = new byte[size];
+	in->read(_data, size);
+
+	// Finally, load the data into the parser.
+	if (!_parser->loadMusic(_data, size)) {
+		warning("MidiPlayer::load - Failed to parse music data");
+	}
+	_parser->setTrack(0);
+}
+
+void MidiPlayer::play() {
+	Common::StackLock lock(_mutex);
+
+	_parser->startPlaying();
+}
+
+void MidiPlayer::pause(bool pause) {
+	if (_paused == pause || !_driver)
+		return;
+
+	_paused = pause;
+
+	Common::StackLock lock(_mutex);
+
+	if (_paused) {
+		_parser->pausePlaying();
+	} else {
+		_parser->resumePlaying();
+	}
+}
+
+void MidiPlayer::stop() {
+	Common::StackLock lock(_mutex);
+
+	_parser->stopPlaying();
+	if (_driver)
+		_driver->deinitSource(0);
+}
+
+void MidiPlayer::setLoop(bool loop) {
+	Common::StackLock lock(_mutex);
+
+	_parser->property(MidiParser::mpAutoLoop, loop);
+}
+
+bool MidiPlayer::isPlaying() {
+	Common::StackLock lock(_mutex);
+
+	return _parser->isPlaying();
+}
+
+void MidiPlayer::syncSoundSettings() {
+	if (_driver)
+		_driver->syncSoundSettings();
+}
+
+void MidiPlayer::onTimer(void *data) {
+	MidiPlayer *p = (MidiPlayer *)data;
+	Common::StackLock lock(p->_mutex);
+
+	p->_parser->onTimer();
 }
 
 void midi_play(const char *name) {
-	g_engine->_midiPlayer.play(name);
+	// Open up the MIDI file
+	Common::SeekableReadStream *f = env_open(name);
+	if (!f) {
+		warning("MIDI not found - %s", name);
+		return;
+	}
+	g_engine->_midiPlayer.load(f, f->size());
+	g_engine->_midiPlayer.play();
 	midi_playing = true;
 }
 
@@ -75,6 +220,14 @@ void midi_stop() {
 	midi_playing = false;
 }
 
+void midi_loop() {
+	// This is probably what this function is supposed to do.
+	// Better to use setLoop to control looping instead of
+	// setting global 9 and calling this function repeatedly.
+	if (midi_playing && !g_engine->_midiPlayer.isPlaying())
+		g_engine->_midiPlayer.play();
+}
+
 } // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/forest/midi.h b/engines/mads/madsv2/forest/midi.h
index a85b3a07544..2b4f78257e1 100644
--- a/engines/mads/madsv2/forest/midi.h
+++ b/engines/mads/madsv2/forest/midi.h
@@ -22,7 +22,8 @@
 #ifndef MADS_FOREST_MIDI_H
 #define MADS_FOREST_MIDI_H
 
-#include "audio/midiplayer.h"
+#include "audio/mididrv_ms.h"
+#include "audio/midiparser.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -30,21 +31,39 @@ namespace Forest {
 
 extern bool midi_playing;
 
-class MidiPlayer : public Audio::MidiPlayer {
+class MidiPlayer {
+public:
+	MidiPlayer();
+	~MidiPlayer();
+
+	int open();
+	void load(Common::SeekableReadStream *in, int64 size = -1);
+	void play();
+	void pause(bool pause);
+	void stop();
+
+	void setLoop(bool loop);
+	bool isPlaying();
+
+	void syncSoundSettings();
+
 private:
-	// MidiDriver_BASE interface implementation
-	void send(uint32 b) override;
+	Common::Mutex _mutex;
+	MidiDriver_Multisource *_driver;
+	MusicType _deviceType;
 
-public:
-	MidiPlayer() : Audio::MidiPlayer() {}
-	~MidiPlayer() override {}
+	MidiParser *_parser;
+	byte *_data;
+
+	bool _paused;
 
-	void play(const char *name);
+protected:
+	static void onTimer(void *data);
 };
 
 extern void midi_play(const char *name);
 extern void midi_stop();
-inline void midi_loop() {}
+extern void midi_loop();
 
 } // namespace Forest
 } // namespace MADSV2




More information about the Scummvm-git-logs mailing list