[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