[Scummvm-git-logs] scummvm master -> 8cb5916e895feb803bd736c388a759f4823f3368
NMIError
noreply at scummvm.org
Fri Jan 9 21:49:06 UTC 2026
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
9e9ae8f989 AUDIO/MIDI: Add HMI HMP parser
16021364a3 M4: Music work-in-progress
95af538d94 AUDIO/MIDI: Fix const
46ac37b4ef MIDI/AGOS: Refactor default MT-32 instruments
086fc1b6ae AUDIO/M4: Add HMI SOS AdLib driver
8cb5916e89 AUDIO: Comment out spammy debug message
Commit: 9e9ae8f9895248d2f58c4b33e980d79f4ffb22e3
https://github.com/scummvm/scummvm/commit/9e9ae8f9895248d2f58c4b33e980d79f4ffb22e3
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-01-09T22:48:47+01:00
Commit Message:
AUDIO/MIDI: Add HMI HMP parser
Changed paths:
A audio/midiparser_hmp.cpp
A audio/midiparser_hmp.h
audio/mididrv.h
audio/mididrv_ms.cpp
audio/mididrv_ms.h
audio/midiparser.h
audio/midiparser_smf.cpp
audio/midiparser_smf.h
audio/module.mk
diff --git a/audio/mididrv.h b/audio/mididrv.h
index 5cb6fe4079d..c394e84b99a 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -166,6 +166,8 @@ public:
static const uint8 MT32_PITCH_BEND_SENSITIVITY_DEFAULT = 0x0C;
static const uint8 GM_PITCH_BEND_SENSITIVITY_DEFAULT = 0x02;
+ // Default reverb value on the Roland SC-55
+ static const uint8 GM_REVERB_DEFAULT = 0x28;
static const uint8 GS_RHYTHM_FIRST_NOTE = 0x1B;
static const uint8 GS_RHYTHM_LAST_NOTE = 0x58;
diff --git a/audio/mididrv_ms.cpp b/audio/mididrv_ms.cpp
index 778e841fd1b..248c05be88b 100644
--- a/audio/mididrv_ms.cpp
+++ b/audio/mididrv_ms.cpp
@@ -47,6 +47,8 @@ MidiDriver_Multisource::ControllerDefaults::ControllerDefaults() :
panning(-1),
expression(-1),
sustain(-1),
+ reverb(-1),
+ chorus(-1),
rpn(-1),
pitchBendSensitivity(-1) {
Common::fill(program, program + ARRAYSIZE(program), -1);
@@ -216,6 +218,9 @@ void MidiDriver_Multisource::setControllerDefault(ControllerDefaultType type) {
case CONTROLLER_DEFAULT_EXPRESSION:
setControllerDefault(type, MIDI_EXPRESSION_DEFAULT);
break;
+ case CONTROLLER_DEFAULT_REVERB:
+ setControllerDefault(type, GM_REVERB_DEFAULT);
+ break;
case CONTROLLER_DEFAULT_RPN:
setControllerDefault(type, MIDI_RPN_NULL);
break;
@@ -228,6 +233,7 @@ void MidiDriver_Multisource::setControllerDefault(ControllerDefaultType type) {
case CONTROLLER_DEFAULT_CHANNEL_PRESSURE:
case CONTROLLER_DEFAULT_MODULATION:
case CONTROLLER_DEFAULT_SUSTAIN:
+ case CONTROLLER_DEFAULT_CHORUS:
default:
setControllerDefault(type, 0);
break;
@@ -268,6 +274,12 @@ void MidiDriver_Multisource::setControllerDefault(ControllerDefaultType type, in
case CONTROLLER_DEFAULT_SUSTAIN:
_controllerDefaults.sustain = value;
break;
+ case CONTROLLER_DEFAULT_REVERB:
+ _controllerDefaults.reverb = value;
+ break;
+ case CONTROLLER_DEFAULT_CHORUS:
+ _controllerDefaults.chorus = value;
+ break;
case CONTROLLER_DEFAULT_RPN:
_controllerDefaults.rpn = value;
break;
diff --git a/audio/mididrv_ms.h b/audio/mididrv_ms.h
index 7aab2919b27..e2047e1e032 100644
--- a/audio/mididrv_ms.h
+++ b/audio/mididrv_ms.h
@@ -156,7 +156,9 @@ public:
CONTROLLER_DEFAULT_EXPRESSION,
CONTROLLER_DEFAULT_SUSTAIN,
CONTROLLER_DEFAULT_RPN,
- CONTROLLER_DEFAULT_PITCH_BEND_SENSITIVITY
+ CONTROLLER_DEFAULT_PITCH_BEND_SENSITIVITY,
+ CONTROLLER_DEFAULT_REVERB,
+ CONTROLLER_DEFAULT_CHORUS
};
protected:
@@ -201,6 +203,8 @@ protected:
int8 panning;
int8 expression;
int8 sustain;
+ int8 reverb;
+ int8 chorus;
int16 rpn;
int8 pitchBendSensitivity;
diff --git a/audio/midiparser.h b/audio/midiparser.h
index d045f7fc46d..d8ee41aca0c 100644
--- a/audio/midiparser.h
+++ b/audio/midiparser.h
@@ -596,7 +596,8 @@ public:
static MidiParser *createParser_SMF(int8 source = -1);
static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0, int source = -1);
static MidiParser *createParser_QT(int8 source = -1);
- static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); }
+ static MidiParser *createParser_HMP(int8 source = -1);
+ static void timerCallback(void *data) { ((MidiParser *)data)->onTimer(); }
};
/** @} */
#endif
diff --git a/audio/midiparser_hmp.cpp b/audio/midiparser_hmp.cpp
new file mode 100644
index 00000000000..8936ae031cf
--- /dev/null
+++ b/audio/midiparser_hmp.cpp
@@ -0,0 +1,157 @@
+/* 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 "audio/midiparser_hmp.h"
+
+#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";
+
+MidiParser_HMP::MidiParser_HMP(int8 source) : MidiParser_SMF(source) {
+}
+
+uint32 MidiParser_HMP::readDelta(byte*& data) {
+ byte str;
+ uint32 value = 0;
+
+ for (int i = 0; i < 4; ++i) {
+ str = *data++;
+ value |= ((str & 0x7F) << (7 * i));
+ if (str & 0x80)
+ break;
+ }
+ return value;
+}
+
+bool MidiParser_HMP::loadMusic(byte *data, uint32 size) {
+ unloadMusic();
+ byte *pos = data;
+
+ // Process header
+ if (memcmp(pos, HMP_HEADER, 8)) {
+ warning("Could not find HMIMIDIP header in HMP data");
+ return false;
+ }
+ pos += 8;
+ _version = determineVersion(pos);
+ // Skip version and padding bytes
+ pos += 24;
+
+ _branchOffset = READ_LE_UINT32(pos);
+ // Skip 3 reserved dwords
+ pos += 16;
+ _numTracks = 1;
+ _numSubtracks[0] = READ_LE_UINT32(pos);
+ pos += 4;
+ // Doesn't seem like this field is actually used...
+ uint32 ppqn = READ_LE_UINT32(pos);
+ _ppqn = 60;
+ pos += 4;
+ uint32 bpm = READ_LE_UINT32(pos);
+ setTempo(60000000 / bpm);
+ pos += 4;
+ _songLength = READ_LE_UINT32(pos);
+ pos += 4;
+
+ for (int i = 0; i < 16; i++) {
+ _channelPriorities[i] = READ_LE_UINT32(pos);
+ pos += 4;
+ }
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 32; j++) {
+ _deviceTrackMappings[j][i] = READ_LE_UINT32(pos);
+ pos += 4;
+ }
+ }
+ if (_version == HmpVersion::VERSION_013195) {
+ Common::copy(pos, pos + 128, _restoreControllers);
+ pos += 128;
+ }
+
+ _callbackPointer = READ_LE_UINT32(pos);
+ pos += 4;
+ _callbackSegment = READ_LE_UINT32(pos);
+ pos += 4;
+
+ // 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;
+
+ _tracks[0][currTrack] = pos;
+ pos += chunkSize - 12;
+ }
+
+ // TODO Read branching data
+
+ return true;
+}
+
+int32 MidiParser_HMP::determineDataSize(Common::SeekableReadStream *stream) {
+ int64 startPos = stream->pos();
+
+ // Process header
+ if (strcmp(stream->readString('\x00', 8).c_str(), HMP_HEADER)) {
+ return -1;
+ }
+ byte versionBytes[6];
+ stream->readMultipleLE(*versionBytes);
+ HmpVersion version = determineVersion(versionBytes);
+ stream->skip(18);
+
+ // TODO Figure out size of branching data
+ uint32 branchOffset = stream->readUint32LE();
+ stream->skip(12);
+
+ uint32 numTracks = stream->readUint32LE();
+ stream->skip(version == HmpVersion::VERSION_013195 ? 852 : 724);
+
+ // Read tracks
+ for (uint currTrack = 0; currTrack < numTracks; currTrack++) {
+ stream->skip(4);
+ uint32 chunkSize = stream->readUint32LE();
+ stream->skip(chunkSize - 8);
+ }
+
+ // stream should now be at the end of the HMP data
+ // (other than branching data, which might be at the end)
+ return stream->pos() - startPos;
+}
+
+MidiParser_HMP::HmpVersion MidiParser_HMP::determineVersion(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;
+ } 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;
+ }
+}
+
+MidiParser *MidiParser::createParser_HMP(int8 source) { return new MidiParser_HMP(source); }
diff --git a/audio/midiparser_hmp.h b/audio/midiparser_hmp.h
new file mode 100644
index 00000000000..4dd943662fc
--- /dev/null
+++ b/audio/midiparser_hmp.h
@@ -0,0 +1,62 @@
+/* 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 AUDIO_MIDIPARSER_HMP_H
+#define AUDIO_MIDIPARSER_HMP_H
+
+#include "audio/midiparser_smf.h"
+
+class MidiParser_HMP : public MidiParser_SMF {
+protected:
+ enum class HmpVersion {
+ VERSION_1,
+ VERSION_013195
+ };
+
+ static const char HMP_HEADER[];
+ static const char HMP_HEADER_VERSION_1[];
+ static const char HMP_HEADER_VERSION_013195[];
+
+ uint32 readDelta(byte *&data) override;
+
+ HmpVersion determineVersion(byte *pos);
+
+public:
+ MidiParser_HMP(int8 source = -1);
+
+ bool loadMusic(byte *data, uint32 size) override;
+
+ int32 determineDataSize(Common::SeekableReadStream *stream) override;
+
+protected:
+ HmpVersion _version;
+ uint32 _branchOffset;
+ // Total track length in seconds
+ uint32 _songLength;
+ uint32 _channelPriorities[16];
+ uint32 _deviceTrackMappings[32][5];
+ uint8 _restoreControllers[128];
+ uint32 _callbackPointer;
+ uint32 _callbackSegment;
+};
+
+#endif
diff --git a/audio/midiparser_smf.cpp b/audio/midiparser_smf.cpp
index f7d1568710a..adb5cdfd473 100644
--- a/audio/midiparser_smf.cpp
+++ b/audio/midiparser_smf.cpp
@@ -29,11 +29,16 @@
MidiParser_SMF::MidiParser_SMF(int8 source) : MidiParser(source) {
}
+uint32 MidiParser_SMF::readDelta(byte *&data) {
+ // Default implementation: use the standard MIDI VLQ format.
+ return readVLQ(data);
+}
+
void MidiParser_SMF::parseNextEvent(EventInfo &info) {
uint8 subtrack = info.subtrack;
const byte *playPos = _position._subtracks[subtrack]._playPos;
info.start = playPos;
- info.delta = readVLQ(playPos);
+ info.delta = readDelta(playPos);
// Process the next info.
if ((playPos[0] & 0xF0) >= 0x80)
diff --git a/audio/midiparser_smf.h b/audio/midiparser_smf.h
index db3c8642fa6..19af97bf005 100644
--- a/audio/midiparser_smf.h
+++ b/audio/midiparser_smf.h
@@ -30,18 +30,9 @@
class MidiParser_SMF : public MidiParser {
protected:
- /**
- * Compresses the specified type 1 MIDI tracks to a single type 0 track.
- *
- * @param tracks Pointer to an array of type 1 tracks.
- * @param numTracks The number of type 1 tracks.
- * @param buffer Buffer which will contain the compressed type 0 track.
- * @param malformedPitchBends True if broken pitch bend events consisting
- * of just the command byte should be ignored. This is only useful for MIDI
- * data which has this specific problem.
- * @return The size of the compressed type 0 track in bytes.
- */
- uint32 compressToType0(byte *tracks[], byte numTracks, byte *buffer, bool malformedPitchBends = false);
+ // Reads a delta between events.
+ virtual uint32 readDelta(byte *&data);
+
void parseNextEvent(EventInfo &info) override;
public:
diff --git a/audio/module.mk b/audio/module.mk
index c1f488efc79..e156a38483e 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -12,6 +12,7 @@ MODULE_OBJS := \
mac_plugin.o \
mididrv.o \
mididrv_ms.o \
+ midiparser_hmp.o \
midiparser_qt.o \
midiparser_smf.o \
midiparser_xmidi.o \
Commit: 16021364a317f5a192f58be38fd892392e7e4b7d
https://github.com/scummvm/scummvm/commit/16021364a317f5a192f58be38fd892392e7e4b7d
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-01-09T22:48:47+01:00
Commit Message:
M4: Music work-in-progress
Changed paths:
engines/m4/adv_r/adv_game.h
engines/m4/m4.cpp
engines/m4/platform/sound/midi.cpp
engines/m4/platform/sound/midi.h
engines/m4/riddle/gui/game_menu.cpp
engines/m4/vars.cpp
diff --git a/engines/m4/adv_r/adv_game.h b/engines/m4/adv_r/adv_game.h
index 80d7e6c2df6..ba5f991d8ac 100644
--- a/engines/m4/adv_r/adv_game.h
+++ b/engines/m4/adv_r/adv_game.h
@@ -38,7 +38,6 @@ struct GameControl {
int16 previous_room = 0;
int32 digi_overall_volume_percent = 100;
- int32 midi_overall_volume_percent = 100;
bool camera_pan_instant = false;
/**
diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp
index 37cb1725528..1c52698974b 100644
--- a/engines/m4/m4.cpp
+++ b/engines/m4/m4.cpp
@@ -84,12 +84,12 @@ int M4Engine::isDemo() const {
Common::Error M4Engine::run() {
// Initialize 320x200 paletted graphics mode
initGraphics(640, 480);
- syncSoundSettings();
_subtitles.load();
// Instantiate globals and setup
Vars *vars = createVars();
+ syncSoundSettings();
if (vars->init()) {
// Set the console
@@ -143,6 +143,8 @@ void M4Engine::syncSoundSettings() {
const int volume = ConfMan.getBool("sfx_mute") ? 0 : CLIP(ConfMan.getInt("sfx_volume"), 0, 255);
_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, volume);
+
+ _G(midi).syncSoundSettings();
}
bool M4Engine::canLoadGameStateCurrently(Common::U32String *msg) {
@@ -266,7 +268,9 @@ Common::Error M4Engine::syncGame(Common::Serializer &s) {
} else {
s.syncAsByte(_G(kernel).restore_game);
s.syncAsSint32LE(_G(game).digi_overall_volume_percent);
- s.syncAsSint32LE(_G(game).midi_overall_volume_percent);
+ int32 midi_volume_percent = ConfMan.getInt("music_volume") * 100 / 255;
+ s.syncAsSint32LE(midi_volume_percent);
+ ConfMan.setInt("music_volume", midi_volume_percent * 255 / 100);
s.syncAsByte(_G(kernel).camera_pan_instant);
}
@@ -292,7 +296,7 @@ Common::Error M4Engine::syncGame(Common::Serializer &s) {
_G(game).previous_room = KERNEL_RESTORING_GAME;
digi_set_overall_volume(_G(game).digi_overall_volume_percent);
- midi_set_overall_volume(_G(game).midi_overall_volume_percent);
+ syncSoundSettings();
interface_show();
}
diff --git a/engines/m4/platform/sound/midi.cpp b/engines/m4/platform/sound/midi.cpp
index 99ef8a66918..779507c92f7 100644
--- a/engines/m4/platform/sound/midi.cpp
+++ b/engines/m4/platform/sound/midi.cpp
@@ -19,31 +19,182 @@
*
*/
-#include "audio/midiparser.h"
#include "m4/platform/sound/midi.h"
#include "m4/adv_r/adv_file.h"
#include "m4/vars.h"
+#include "common/config-manager.h"
+#include "audio/adlib_ms.h"
+#include "audio/fmopl.h"
+#include "audio/midiparser.h"
+#include "audio/midiparser_hmp.h"
+#include "audio/mt32gm.h"
+
namespace M4 {
namespace Sound {
int Midi::_midiEndTrigger;
-Midi::Midi(Audio::Mixer *mixer) : _mixer(mixer) {
- Midi::createDriver();
+Midi::Midi() {
+ _driver = nullptr;
+ _paused = false;
+ _deviceType = MT_NULL;
+ _midiParser = nullptr;
+ _midiData = nullptr;
+}
+
+Midi::~Midi() {
+ stop();
+
+ if (_driver != nullptr) {
+ _driver->setTimerCallback(nullptr, nullptr);
+ _driver->close();
+ }
- int ret = _driver->open();
- if (ret == 0) {
- if (_nativeMT32)
- _driver->sendMT32Reset();
- else
- _driver->sendGMReset();
+ Common::StackLock lock(_mutex);
- _driver->setTimerCallback(this, &timerCallback);
+ if (_midiParser != nullptr)
+ delete _midiParser;
+ if (_midiData != nullptr)
+ delete[] _midiData;
+ if (_driver != nullptr) {
+ delete _driver;
+ _driver = nullptr;
}
}
+int Midi::open() {
+ assert(_driver == nullptr);
+
+ // Check the type of device that the user has configured.
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _deviceType = MidiDriver::getMusicType(dev);
+ if (_deviceType == MT_GM && ConfMan.getBool("native_mt32"))
+ _deviceType = MT_MT32;
+
+ OPL::Config::OplType oplType;
+ switch (_deviceType) {
+ case MT_ADLIB:
+ oplType = MidiDriver_ADLIB_Multisource::detectOplType(OPL::Config::kOpl3) ? OPL::Config::kOpl3 : OPL::Config::kOpl2;
+ _driver = new MidiDriver_ADLIB_Multisource(oplType);
+ break;
+ case MT_GM:
+ case MT_MT32:
+ _driver = new MidiDriver_MT32GM(MusicType::MT_GM);
+ break;
+ default:
+ _driver = new MidiDriver_NULL_Multisource();
+ break;
+ }
+
+ _midiParser = new MidiParser_HMP(0);
+
+ _driver->property(MidiDriver::PROP_USER_VOLUME_SCALING, true);
+ // Riddle's MIDI data does not consistently set values for every controller
+ // at the start of every track
+ _driver->setControllerDefault(MidiDriver_Multisource::CONTROLLER_DEFAULT_PITCH_BEND);
+ _driver->setControllerDefault(MidiDriver_Multisource::CONTROLLER_DEFAULT_MODULATION);
+ _driver->setControllerDefault(MidiDriver_Multisource::CONTROLLER_DEFAULT_PANNING);
+ _driver->setControllerDefault(MidiDriver_Multisource::CONTROLLER_DEFAULT_REVERB);
+ _driver->setControllerDefault(MidiDriver_Multisource::CONTROLLER_DEFAULT_CHORUS);
+
+ _midiParser->property(MidiParser::mpDisableAutoStartPlayback, true);
+ // Riddle's MIDI data uses sustain
+ _midiParser->property(MidiParser::mpSendSustainOffOnNotesOff, true);
+
+ // Open the MIDI driver.
+ int returnCode = _driver->open();
+ if (returnCode != 0)
+ error("Midi::open - Failed to open MIDI music driver - error code %d.", returnCode);
+
+ syncSoundSettings();
+
+ // Connect the driver and the parser.
+ _midiParser->setMidiDriver(_driver);
+ _midiParser->setTimerRate(_driver->getBaseTempo());
+ _driver->setTimerCallback(this, &onTimer);
+
+ return 0;
+}
+
+void Midi::load(byte* in, int32 size) {
+ Common::StackLock lock(_mutex);
+
+ if (_midiParser == nullptr)
+ return;
+
+ _midiParser->unloadMusic();
+
+ if (_midiData != nullptr)
+ delete[] _midiData;
+ _midiData = new byte[size];
+
+ Common::copy(in, in + size, _midiData);
+
+ _midiParser->loadMusic(_midiData, size);
+}
+
+void Midi::play() {
+ Common::StackLock lock(_mutex);
+
+ if (_midiParser == nullptr || _driver == nullptr)
+ return;
+
+ _midiParser->startPlaying();
+}
+
+void Midi::pause(bool pause) {
+ if (_paused == pause || _driver == nullptr)
+ return;
+
+ _paused = pause;
+
+ if (_midiParser != nullptr) {
+ Common::StackLock lock(_mutex);
+ if (_paused) {
+ _midiParser->pausePlaying();
+ } else {
+ _midiParser->resumePlaying();
+ }
+ }
+}
+
+void Midi::stop() {
+ Common::StackLock lock(_mutex);
+
+ if (_midiParser != nullptr) {
+ _midiParser->stopPlaying();
+ if (_driver != nullptr)
+ _driver->deinitSource(0);
+ }
+}
+
+bool Midi::isPlaying() {
+ Common::StackLock lock(_mutex);
+
+ return _midiParser->isPlaying();
+}
+
+void Midi::startFade(uint16 duration, uint16 targetVolume) {
+ if (_driver == nullptr || _midiParser == nullptr || !_midiParser->isPlaying())
+ return;
+
+ _driver->startFade(0, duration, targetVolume);
+}
+
+bool Midi::isFading() {
+ return _driver->isFading(0);
+}
+
+void Midi::syncSoundSettings() {
+ if (_driver != nullptr)
+ _driver->syncSoundSettings();
+}
+
void Midi::midi_play(const char *name, int volume, bool loop, int trigger, int roomNum) {
+ if (_driver == nullptr || _midiParser == nullptr)
+ return;
+
_midiEndTrigger = trigger;
// Load in the resource
@@ -54,14 +205,22 @@ void Midi::midi_play(const char *name, int volume, bool loop, int trigger, int r
error("Could not find music - %s", fileName.c_str());
HLock(workHandle);
-#if 0
-// Dump the midi file for analysis purposes
+ /*
Common::DumpFile dump;
dump.open(fileName.c_str());
dump.write(*workHandle, assetSize);
dump.close();
-#endif
+ */
+
+ load((byte *)*workHandle, assetSize);
+ _midiParser->setTrack(0);
+ _midiParser->property(MidiParser::mpAutoLoop, loop ? 1 : 0);
+ // TODO Some calls use volume 0? What is that supposed to do?
+ _driver->setSourceVolume(0, volume);
+ play();
+
+ /*
#ifdef TODO
byte *pSrc = (byte *)*workHandle;
@@ -84,6 +243,7 @@ void Midi::midi_play(const char *name, int volume, bool loop, int trigger, int r
if (trigger != -1)
kernel_timing_trigger(10, trigger);
#endif
+ */
HUnLock(workHandle);
rtoss(fileName);
@@ -97,12 +257,25 @@ void Midi::loop() {
// No implementation
}
-void Midi::set_overall_volume(int vol) {
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, vol * 255 / 100);
+void Midi::midi_fade_volume(int targetVolume, int duration) {
+ uint16 durationMsec = duration * 1000 / 30;
+ startFade(durationMsec, targetVolume);
+ // TODO Should this stop playback when fade is completed?
+ // Should this call return after the fade has completed?
}
-int Midi::get_overall_volume() const {
- return _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) * 100 / 255;
+void Midi::onTimer(void* data) {
+ Midi *m = (Midi *)data;
+ Common::StackLock lock(m->_mutex);
+
+ if (m->_midiParser != nullptr) {
+ m->_midiParser->onTimer();
+ if (!m->_midiParser->isPlaying() && _midiEndTrigger >= 0) {
+ // FIXME Can this trigger a deadlock on the mutex?
+ kernel_timing_trigger(10, _midiEndTrigger);
+ _midiEndTrigger = -1;
+ }
+ }
}
} // namespace Sound
@@ -119,16 +292,8 @@ void midi_stop() {
_G(midi).stop();
}
-void midi_set_overall_volume(int vol) {
- _G(midi).set_overall_volume(vol);
-}
-
-int midi_get_overall_volume() {
- return _G(midi).get_overall_volume();
-}
-
-void midi_fade_volume(int val1, int val2) {
- warning("TODO: midi_fade_volume");
+void midi_fade_volume(int targetVolume, int duration) {
+ _G(midi).midi_fade_volume(targetVolume, duration);
}
} // namespace M4
diff --git a/engines/m4/platform/sound/midi.h b/engines/m4/platform/sound/midi.h
index 04f6ee93f09..f5f8b220c0f 100644
--- a/engines/m4/platform/sound/midi.h
+++ b/engines/m4/platform/sound/midi.h
@@ -23,24 +23,51 @@
#ifndef M4_SOUND_PLATFORM_MIDI_H
#define M4_SOUND_PLATFORM_MIDI_H
-#include "audio/midiplayer.h"
#include "m4/m4_types.h"
+#include "audio/mididrv_ms.h"
+#include "audio/midiparser.h"
+
namespace M4 {
namespace Sound {
-class Midi : public Audio::MidiPlayer {
+class Midi {
private:
static int _midiEndTrigger;
- Audio::Mixer *_mixer;
+
+ Common::Mutex _mutex;
+
+ MusicType _deviceType;
+
+ MidiDriver_Multisource *_driver;
+ MidiParser *_midiParser;
+ byte *_midiData;
+
+ bool _paused;
+
+protected:
+ static void onTimer(void *data);
+
public:
- Midi(Audio::Mixer *mixer);
+ Midi();
+ ~Midi();
+
+ int open();
+
+ void load(byte *in, int32 size);
+ void play();
+ void pause(bool pause);
+ void stop();
+ bool isPlaying();
+ void startFade(uint16 duration, uint16 targetVolume);
+ bool isFading();
+
+ void syncSoundSettings();
void midi_play(const char *name, int volume, bool loop, int trigger, int roomNum);
void task();
void loop();
- void set_overall_volume(int vol);
- int get_overall_volume() const;
+ void midi_fade_volume(int targetVolume, int duration);
};
} // namespace Sound
@@ -48,9 +75,7 @@ public:
void midi_play(const char *name, int volume, bool loop, int trigger, int roomNum);
void midi_loop();
void midi_stop();
-void midi_set_overall_volume(int vol);
-int midi_get_overall_volume();
-void midi_fade_volume(int val1, int val2);
+void midi_fade_volume(int targetVolume, int duration);
} // namespace M4
diff --git a/engines/m4/riddle/gui/game_menu.cpp b/engines/m4/riddle/gui/game_menu.cpp
index 0d1183ec0b5..f58722279e0 100644
--- a/engines/m4/riddle/gui/game_menu.cpp
+++ b/engines/m4/riddle/gui/game_menu.cpp
@@ -32,6 +32,7 @@
#include "m4/mem/mem.h"
#include "m4/platform/keys.h"
#include "m4/m4.h"
+#include "common/config-manager.h"
#include "m4/burger/gui/game_menu.h"
@@ -243,7 +244,7 @@ void OptionsMenu::show() {
assert(_GM(opMenu));
const int digiPercent = digi_get_overall_volume();
- const int midiPercent = midi_get_overall_volume();
+ const int midiPercent = ConfMan.getInt("music_volume") * 100 / 255;
menuItemButton::add(_GM(opMenu), OM_TAG_GAMEMENU,
OM_GAMEMENU_X, OM_GAMEMENU_Y, OM_GAMEMENU_W, OM_GAMEMENU_H,
@@ -291,7 +292,7 @@ void OptionsMenu::cbSetDigi(M4::GUI::menuItemHSlider *myItem, M4::GUI::guiMenu *
}
void OptionsMenu::cbSetMidi(M4::GUI::menuItemHSlider *myItem, M4::GUI::guiMenu *) {
- midi_set_overall_volume(myItem->percent);
+ ConfMan.setInt("music_volume", myItem->percent * 255 / 100);
}
/*------------------- SAVE/LOAD METHODS ------------------*/
diff --git a/engines/m4/vars.cpp b/engines/m4/vars.cpp
index f316478839a..ebfb4c8903a 100644
--- a/engines/m4/vars.cpp
+++ b/engines/m4/vars.cpp
@@ -41,7 +41,7 @@ namespace M4 {
Vars *g_vars;
-Vars::Vars() : _digi(g_engine->_mixer), _midi(g_engine->_mixer) {
+Vars::Vars() : _digi(g_engine->_mixer), _midi() {
g_vars = this;
Common::fill(_sizeMem, _sizeMem + _MEMTYPE_LIMIT, 0);
@@ -134,7 +134,9 @@ void Vars::game_systems_initialize(byte flags) {
fire_up_gui();
if (flags & INSTALL_SOUND_DRIVERS) {
- // No implementation
+ int result = _midi.open();
+ if (result > 0)
+ warning("MIDI Player init failed: \"%s\"", MidiDriver::getErrorName(result));
} else {
term_message("Sound driver installation skipped");
}
Commit: 95af538d94301932529a74757e3ef1fdaa76e51f
https://github.com/scummvm/scummvm/commit/95af538d94301932529a74757e3ef1fdaa76e51f
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-01-09T22:48:47+01:00
Commit Message:
AUDIO/MIDI: Fix const
Changed paths:
audio/midiparser_hmp.cpp
audio/midiparser_hmp.h
audio/midiparser_smf.cpp
audio/midiparser_smf.h
diff --git a/audio/midiparser_hmp.cpp b/audio/midiparser_hmp.cpp
index 8936ae031cf..69b51057837 100644
--- a/audio/midiparser_hmp.cpp
+++ b/audio/midiparser_hmp.cpp
@@ -31,7 +31,7 @@ const char MidiParser_HMP::HMP_HEADER_VERSION_013195[] = "013195";
MidiParser_HMP::MidiParser_HMP(int8 source) : MidiParser_SMF(source) {
}
-uint32 MidiParser_HMP::readDelta(byte*& data) {
+uint32 MidiParser_HMP::readDelta(const byte*& data) {
byte str;
uint32 value = 0;
@@ -44,9 +44,9 @@ uint32 MidiParser_HMP::readDelta(byte*& data) {
return value;
}
-bool MidiParser_HMP::loadMusic(byte *data, uint32 size) {
+bool MidiParser_HMP::loadMusic(const byte *data, uint32 size) {
unloadMusic();
- byte *pos = data;
+ const byte *pos = data;
// Process header
if (memcmp(pos, HMP_HEADER, 8)) {
@@ -143,7 +143,7 @@ int32 MidiParser_HMP::determineDataSize(Common::SeekableReadStream *stream) {
return stream->pos() - startPos;
}
-MidiParser_HMP::HmpVersion MidiParser_HMP::determineVersion(byte *pos) {
+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)) {
diff --git a/audio/midiparser_hmp.h b/audio/midiparser_hmp.h
index 4dd943662fc..12dbe2027cb 100644
--- a/audio/midiparser_hmp.h
+++ b/audio/midiparser_hmp.h
@@ -36,14 +36,14 @@ protected:
static const char HMP_HEADER_VERSION_1[];
static const char HMP_HEADER_VERSION_013195[];
- uint32 readDelta(byte *&data) override;
+ uint32 readDelta(const byte *&data) override;
- HmpVersion determineVersion(byte *pos);
+ HmpVersion determineVersion(const byte *pos);
public:
MidiParser_HMP(int8 source = -1);
- bool loadMusic(byte *data, uint32 size) override;
+ bool loadMusic(const byte *data, uint32 size) override;
int32 determineDataSize(Common::SeekableReadStream *stream) override;
diff --git a/audio/midiparser_smf.cpp b/audio/midiparser_smf.cpp
index adb5cdfd473..2bf7e1f0932 100644
--- a/audio/midiparser_smf.cpp
+++ b/audio/midiparser_smf.cpp
@@ -29,7 +29,7 @@
MidiParser_SMF::MidiParser_SMF(int8 source) : MidiParser(source) {
}
-uint32 MidiParser_SMF::readDelta(byte *&data) {
+uint32 MidiParser_SMF::readDelta(const byte *&data) {
// Default implementation: use the standard MIDI VLQ format.
return readVLQ(data);
}
diff --git a/audio/midiparser_smf.h b/audio/midiparser_smf.h
index 19af97bf005..f2da401096b 100644
--- a/audio/midiparser_smf.h
+++ b/audio/midiparser_smf.h
@@ -31,7 +31,7 @@ class MidiParser_SMF : public MidiParser {
protected:
// Reads a delta between events.
- virtual uint32 readDelta(byte *&data);
+ virtual uint32 readDelta(const byte *&data);
void parseNextEvent(EventInfo &info) override;
Commit: 46ac37b4efe4f794f1c386b1344a90af854e7a67
https://github.com/scummvm/scummvm/commit/46ac37b4efe4f794f1c386b1344a90af854e7a67
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-01-09T22:48:47+01:00
Commit Message:
MIDI/AGOS: Refactor default MT-32 instruments
Changed paths:
audio/adlib_ms.cpp
audio/mididrv_ms.cpp
audio/mididrv_ms.h
audio/mt32gm.cpp
audio/mt32gm.h
engines/agos/midi.cpp
diff --git a/audio/adlib_ms.cpp b/audio/adlib_ms.cpp
index d13015b0762..dc79e224f7e 100644
--- a/audio/adlib_ms.cpp
+++ b/audio/adlib_ms.cpp
@@ -2089,7 +2089,7 @@ void MidiDriver_ADLIB_Multisource::writeFrequency(uint8 oplChannel, OplInstrumen
}
void MidiDriver_ADLIB_Multisource::writeRegister(uint16 reg, uint8 value, bool forceWrite) {
- //debug("Writing register %X %X", reg, value);
+ debug("Writing register %X %X", reg, value);
// Write the value to the register if it is a timer register, if forceWrite
// is specified or if the new register value is different from the current
diff --git a/audio/mididrv_ms.cpp b/audio/mididrv_ms.cpp
index 248c05be88b..a214c007883 100644
--- a/audio/mididrv_ms.cpp
+++ b/audio/mididrv_ms.cpp
@@ -292,7 +292,7 @@ void MidiDriver_Multisource::setControllerDefault(ControllerDefaultType type, in
}
}
-void MidiDriver_Multisource::setControllerDefaults(ControllerDefaultType type, int16 *value) {
+void MidiDriver_Multisource::setControllerDefaults(ControllerDefaultType type, const int16 *value) {
// Set the specified default values on _controllerDefaults on the field
// corresponding to the specified controller.
switch (type) {
diff --git a/audio/mididrv_ms.h b/audio/mididrv_ms.h
index e2047e1e032..07b665df992 100644
--- a/audio/mididrv_ms.h
+++ b/audio/mididrv_ms.h
@@ -370,7 +370,7 @@ public:
* @param values The default values which should be set. Must be a 16 value
* array.
*/
- void setControllerDefaults(ControllerDefaultType type, int16 *values);
+ void setControllerDefaults(ControllerDefaultType type, const int16 *values);
/**
* Clears a previously set default value for the specified controller.
*
diff --git a/audio/mt32gm.cpp b/audio/mt32gm.cpp
index 441acf2f3f3..4cc5457666f 100644
--- a/audio/mt32gm.cpp
+++ b/audio/mt32gm.cpp
@@ -41,6 +41,11 @@ const byte MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS[8] = {
0x44, 0x30, 0x5F, 0x4E, 0x29, 0x03, 0x6E, 0x7A
};
+// Same as above, now numbered by MIDI channel. For use with setControllerDefaults.
+const int16 MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS_CONTROLLER_DEFAULTS[16] = {
+ -1, 0x44, 0x30, 0x5F, 0x4E, 0x29, 0x03, 0x6E, 0x7A, -1, -1, -1, -1, -1, -1, -1
+};
+
// These are the power-on default panning settings for channels 2-9 of the Roland MT-32 family.
// Internally, the MT-32 has 15 panning positions (0-E with 7 being center).
// This has been translated to the equivalent MIDI panning values (0-127).
diff --git a/audio/mt32gm.h b/audio/mt32gm.h
index 43de8d15a9a..ee931bae872 100644
--- a/audio/mt32gm.h
+++ b/audio/mt32gm.h
@@ -111,6 +111,7 @@
class MidiDriver_MT32GM : public MidiDriver_Multisource {
public:
static const byte MT32_DEFAULT_INSTRUMENTS[8];
+ static const int16 MT32_DEFAULT_INSTRUMENTS_CONTROLLER_DEFAULTS[16];
static const byte MT32_DEFAULT_PANNING[8];
static const uint8 MT32_DEFAULT_CHANNEL_VOLUME = 102;
static const uint8 GM_DEFAULT_CHANNEL_VOLUME = 100;
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index a8f6cb1ef54..6c50733400e 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -250,10 +250,7 @@ int MidiPlayer::open() {
_driverMsMusic = MidiDriver_Accolade_MT32_create(accoladeDriverFilename);
if (_vm->getGameType() == GType_WW) {
// WORKAROUND See above.
- int16 defaultInstruments[16];
- Common::fill(defaultInstruments, defaultInstruments + ARRAYSIZE(defaultInstruments), -1);
- Common::copy(MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS, MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS + ARRAYSIZE(MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS), defaultInstruments + 1);
- _driverMsMusic->setControllerDefaults(MidiDriver_Multisource::CONTROLLER_DEFAULT_PROGRAM, defaultInstruments);
+ _driverMsMusic->setControllerDefaults(MidiDriver_Multisource::CONTROLLER_DEFAULT_PROGRAM, MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS_CONTROLLER_DEFAULTS);
}
if (usesMidiSfx) {
if (ConfMan.getBool("multi_midi")) {
@@ -355,10 +352,7 @@ int MidiPlayer::open() {
_driverMsMusic = new MidiDriver_MT32GM(_dataType);
// WORKAROUND See above.
- int16 defaultInstruments[16];
- Common::fill(defaultInstruments, defaultInstruments + ARRAYSIZE(defaultInstruments), -1);
- Common::copy(MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS, MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS + ARRAYSIZE(MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS), defaultInstruments + 1);
- _driverMsMusic->setControllerDefaults(MidiDriver_Multisource::CONTROLLER_DEFAULT_PROGRAM, defaultInstruments);
+ _driverMsMusic->setControllerDefaults(MidiDriver_Multisource::CONTROLLER_DEFAULT_PROGRAM, MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS_CONTROLLER_DEFAULTS);
if (_vm->getPlatform() == Common::kPlatformDOS && !(_vm->getFeatures() & GF_TALKIE) &&
ConfMan.getBool("multi_midi")) {
Commit: 086fc1b6aec49703d05f8ffce5a7fd7b243b52ad
https://github.com/scummvm/scummvm/commit/086fc1b6aec49703d05f8ffce5a7fd7b243b52ad
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-01-09T22:48:47+01:00
Commit Message:
AUDIO/M4: Add HMI SOS AdLib driver
Changed paths:
A audio/adlib_hmisos.cpp
A audio/adlib_hmisos.h
audio/midiparser_hmp.cpp
audio/midiparser_hmp.h
audio/module.mk
engines/m4/platform/sound/midi.cpp
diff --git a/audio/adlib_hmisos.cpp b/audio/adlib_hmisos.cpp
new file mode 100644
index 00000000000..58a7626b17b
--- /dev/null
+++ b/audio/adlib_hmisos.cpp
@@ -0,0 +1,130 @@
+/* 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 "audio/adlib_hmisos.h"
+
+const byte MidiDriver_ADLIB_HMISOS::INSTRUMENT_BANK_SIGNATURE[8] = { 0x00, 0x00, 'A', 'D', 'L', 'I', 'B', '-' };
+
+MidiDriver_ADLIB_HMISOS::MidiDriver_ADLIB_HMISOS(OPL::Config::OplType oplType, int timerFrequency) :
+ MidiDriver_ADLIB_Multisource(oplType, timerFrequency) {
+ memset(_sosInstrumentBank, 0, sizeof(_sosInstrumentBank));
+ memset(_sosRhythmBank, 0, sizeof(_sosRhythmBank));
+
+ _instrumentWriteMode = INSTRUMENT_WRITE_MODE_NOTE_ON;
+ _modulationDepth = MODULATION_DEPTH_LOW;
+ _vibratoDepth = VIBRATO_DEPTH_LOW;
+ _rhythmModeRewriteSharedRegister = true;
+}
+
+int MidiDriver_ADLIB_HMISOS::open() {
+
+ return MidiDriver_ADLIB_Multisource::open();
+}
+
+bool MidiDriver_ADLIB_HMISOS::loadInstrumentBanks(Common::SeekableReadStream *instrumentBankStream, Common::SeekableReadStream *rhythmBankStream) {
+ int numInstruments = loadInstrumentBank(instrumentBankStream, false);
+ if (numInstruments < 0)
+ return false;
+ _instrumentBank = _sosInstrumentBank;
+
+ if (rhythmBankStream != nullptr) {
+ numInstruments = loadInstrumentBank(rhythmBankStream, true);
+ if (numInstruments < 0)
+ return false;
+ _rhythmBank = _sosRhythmBank;
+ _rhythmBankFirstNote = 0;
+ _rhythmBankLastNote = numInstruments;
+ }
+
+ return true;
+}
+
+int MidiDriver_ADLIB_HMISOS::loadInstrumentBank(Common::SeekableReadStream *stream, bool rhythmBank) {
+ OplInstrumentDefinition *instrumentBank = rhythmBank ? _sosRhythmBank : _sosInstrumentBank;
+
+ byte signature[8];
+ stream->read(signature, 8);
+
+ if (memcmp(INSTRUMENT_BANK_SIGNATURE, signature, 8)) {
+ warning("Invalid HMI SOS AdLib instrument bank signature");
+ return -1;
+ }
+
+ uint16 instrumentsUsed = stream->readUint16LE();
+ if (instrumentsUsed > 128)
+ instrumentsUsed = 128;
+ //uint16 instrumentsTotal = stream->readUint16LE();
+ stream->skip(2);
+ uint32 namesOffset = stream->readUint32LE();
+ uint32 instDataOffset = stream->readUint32LE();
+
+ stream->seek(namesOffset, SEEK_SET);
+ byte rhythmNotes[128];
+ for (uint16 i = 0; i < instrumentsUsed; i++) {
+ stream->skip(2); // Instrument data offset
+ // The flags byte is used to specify the rhythm note
+ rhythmNotes[i] = stream->readByte();
+ stream->skip(9); // Instrument name
+ }
+
+ stream->seek(instDataOffset, SEEK_SET);
+ AdLibBnkInstrumentDefinition bnkInstDef;
+ for (uint16 i = 0; i < instrumentsUsed; i++) {
+ stream->skip(2); // Instrument type and voice number
+
+ bnkInstDef.operator0.keyScalingLevel = stream->readByte();
+ bnkInstDef.operator0.frequencyMultiplier = stream->readByte();
+ bnkInstDef.operator0.feedback = stream->readByte();
+ bnkInstDef.operator0.attack = stream->readByte();
+ bnkInstDef.operator0.sustain = stream->readByte();
+ bnkInstDef.operator0.envelopeGainType = stream->readByte();
+ bnkInstDef.operator0.decay = stream->readByte();
+ bnkInstDef.operator0.release = stream->readByte();
+ bnkInstDef.operator0.level = stream->readByte();
+ bnkInstDef.operator0.amplitudeModulation = stream->readByte();
+ bnkInstDef.operator0.vibrato = stream->readByte();
+ bnkInstDef.operator0.keyScalingRate = stream->readByte();
+ // Connection bit in SOS BNK is not flipped
+ bnkInstDef.operator0.connection = stream->readByte() == 0 ? 1 : 0;
+
+ bnkInstDef.operator1.keyScalingLevel = stream->readByte();
+ bnkInstDef.operator1.frequencyMultiplier = stream->readByte();
+ bnkInstDef.operator1.feedback = stream->readByte();
+ bnkInstDef.operator1.attack = stream->readByte();
+ bnkInstDef.operator1.sustain = stream->readByte();
+ bnkInstDef.operator1.envelopeGainType = stream->readByte();
+ bnkInstDef.operator1.decay = stream->readByte();
+ bnkInstDef.operator1.release = stream->readByte();
+ bnkInstDef.operator1.level = stream->readByte();
+ bnkInstDef.operator1.amplitudeModulation = stream->readByte();
+ bnkInstDef.operator1.vibrato = stream->readByte();
+ bnkInstDef.operator1.keyScalingRate = stream->readByte();
+ bnkInstDef.operator1.connection = stream->readByte() == 0 ? 1 : 0;
+
+ bnkInstDef.waveformSelect0 = stream->readByte();
+ bnkInstDef.waveformSelect1 = stream->readByte();
+
+ bnkInstDef.toOplInstrumentDefinition(instrumentBank[i]);
+ instrumentBank[i].rhythmNote = rhythmNotes[i];
+ }
+
+ return instrumentsUsed;
+}
diff --git a/audio/adlib_hmisos.h b/audio/adlib_hmisos.h
new file mode 100644
index 00000000000..f959673d3c2
--- /dev/null
+++ b/audio/adlib_hmisos.h
@@ -0,0 +1,57 @@
+/* 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 AUDIO_ADLIB_HMISOS_H
+#define AUDIO_ADLIB_HMISOS_H
+
+#include "audio/adlib_ms.h"
+
+#include "common/stream.h"
+
+/**
+ * General MIDI AdLib driver based on HMI Sound Operating System version dated 1994-11-27
+ * (as used by The Riddle Of Master Lu).
+ *
+ * Currently, only instrument bank loading is implemented.
+ * TODO:
+ * - Frequency calculation
+ * - Level calculation
+ * - Channel allocation
+ * - Controller 0x66 (pitch bend range)
+ */
+class MidiDriver_ADLIB_HMISOS : public MidiDriver_ADLIB_Multisource {
+public:
+ static const byte INSTRUMENT_BANK_SIGNATURE[8];
+
+ MidiDriver_ADLIB_HMISOS(OPL::Config::OplType oplType, int timerFrequency = OPL::OPL::kDefaultCallbackFrequency);
+ ~MidiDriver_ADLIB_HMISOS() {};
+
+ int open() override;
+ bool loadInstrumentBanks(Common::SeekableReadStream *instrumentBankStream, Common::SeekableReadStream *rhythmBankStream = nullptr);
+
+protected:
+ int loadInstrumentBank(Common::SeekableReadStream *instrumentBankStream, bool rhythmBank);
+
+ OplInstrumentDefinition _sosInstrumentBank[128];
+ OplInstrumentDefinition _sosRhythmBank[128];
+};
+
+#endif
diff --git a/audio/midiparser_hmp.cpp b/audio/midiparser_hmp.cpp
index 69b51057837..deea89ae38d 100644
--- a/audio/midiparser_hmp.cpp
+++ b/audio/midiparser_hmp.cpp
@@ -29,6 +29,14 @@ const char MidiParser_HMP::HMP_HEADER_VERSION_1[] = "\x00\x00\x00\x00\x00\x00";
const char MidiParser_HMP::HMP_HEADER_VERSION_013195[] = "013195";
MidiParser_HMP::MidiParser_HMP(int8 source) : MidiParser_SMF(source) {
+ _version = HmpVersion::VERSION_1;
+ _branchOffset = 0;
+ _songLength = 0;
+ memset(_channelPriorities, 0, sizeof(_channelPriorities));
+ memset(_deviceTrackMappings, 0, sizeof(_deviceTrackMappings));
+ memset(_restoreControllers, 0, sizeof(_restoreControllers));
+ _callbackPointer = 0;
+ _callbackSegment = 0;
}
uint32 MidiParser_HMP::readDelta(const byte*& data) {
@@ -65,7 +73,7 @@ bool MidiParser_HMP::loadMusic(const byte *data, uint32 size) {
_numSubtracks[0] = READ_LE_UINT32(pos);
pos += 4;
// Doesn't seem like this field is actually used...
- uint32 ppqn = READ_LE_UINT32(pos);
+ //uint32 ppqn = READ_LE_UINT32(pos);
_ppqn = 60;
pos += 4;
uint32 bpm = READ_LE_UINT32(pos);
@@ -96,11 +104,11 @@ bool MidiParser_HMP::loadMusic(const byte *data, uint32 size) {
// Read the tracks
for (uint currTrack = 0; currTrack < _numSubtracks[0]; currTrack++) {
- uint32 chunkNumber = READ_LE_UINT32(pos);
+ //uint32 chunkNumber = READ_LE_UINT32(pos);
pos += 4;
uint32 chunkSize = READ_LE_UINT32(pos);
pos += 4;
- uint32 trackNumber = READ_LE_UINT32(pos);
+ //uint32 trackNumber = READ_LE_UINT32(pos);
pos += 4;
_tracks[0][currTrack] = pos;
@@ -125,7 +133,8 @@ int32 MidiParser_HMP::determineDataSize(Common::SeekableReadStream *stream) {
stream->skip(18);
// TODO Figure out size of branching data
- uint32 branchOffset = stream->readUint32LE();
+ //uint32 branchOffset = stream->readUint32LE();
+ stream->skip(4);
stream->skip(12);
uint32 numTracks = stream->readUint32LE();
diff --git a/audio/midiparser_hmp.h b/audio/midiparser_hmp.h
index 12dbe2027cb..bc2e863d166 100644
--- a/audio/midiparser_hmp.h
+++ b/audio/midiparser_hmp.h
@@ -19,12 +19,17 @@
*
*/
-
#ifndef AUDIO_MIDIPARSER_HMP_H
#define AUDIO_MIDIPARSER_HMP_H
#include "audio/midiparser_smf.h"
+/**
+ * MIDI parser for the HMI SOS format HMP.
+ *
+ * Implementation is incomplete. Currently only the track chunks are read and
+ * played back. Not implented yet: device track mapping, branching, callbacks, etc.
+ */
class MidiParser_HMP : public MidiParser_SMF {
protected:
enum class HmpVersion {
diff --git a/audio/module.mk b/audio/module.mk
index e156a38483e..14fc47c208e 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -3,6 +3,7 @@ MODULE := audio
MODULE_OBJS := \
adlib.o \
adlib_ctmidi.o \
+ adlib_hmisos.o \
adlib_ms.o \
audiostream.o \
casio.o \
diff --git a/engines/m4/platform/sound/midi.cpp b/engines/m4/platform/sound/midi.cpp
index 779507c92f7..cede69aaec4 100644
--- a/engines/m4/platform/sound/midi.cpp
+++ b/engines/m4/platform/sound/midi.cpp
@@ -24,6 +24,7 @@
#include "m4/vars.h"
#include "common/config-manager.h"
+#include "audio/adlib_hmisos.h"
#include "audio/adlib_ms.h"
#include "audio/fmopl.h"
#include "audio/midiparser.h"
@@ -75,8 +76,18 @@ int Midi::open() {
OPL::Config::OplType oplType;
switch (_deviceType) {
case MT_ADLIB:
- oplType = MidiDriver_ADLIB_Multisource::detectOplType(OPL::Config::kOpl3) ? OPL::Config::kOpl3 : OPL::Config::kOpl2;
- _driver = new MidiDriver_ADLIB_Multisource(oplType);
+ oplType = MidiDriver_ADLIB_HMISOS::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:
Commit: 8cb5916e895feb803bd736c388a759f4823f3368
https://github.com/scummvm/scummvm/commit/8cb5916e895feb803bd736c388a759f4823f3368
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-01-09T22:48:47+01:00
Commit Message:
AUDIO: Comment out spammy debug message
Changed paths:
audio/adlib_ms.cpp
diff --git a/audio/adlib_ms.cpp b/audio/adlib_ms.cpp
index dc79e224f7e..d13015b0762 100644
--- a/audio/adlib_ms.cpp
+++ b/audio/adlib_ms.cpp
@@ -2089,7 +2089,7 @@ void MidiDriver_ADLIB_Multisource::writeFrequency(uint8 oplChannel, OplInstrumen
}
void MidiDriver_ADLIB_Multisource::writeRegister(uint16 reg, uint8 value, bool forceWrite) {
- debug("Writing register %X %X", reg, value);
+ //debug("Writing register %X %X", reg, value);
// Write the value to the register if it is a timer register, if forceWrite
// is specified or if the new register value is different from the current
More information about the Scummvm-git-logs
mailing list