[Scummvm-git-logs] scummvm master -> f6c38ae6fbf0110e20e4e6eb6a867f767a9e1000
dreammaster
noreply at scummvm.org
Sat Jun 6 11:38:01 UTC 2026
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
601fc79921 MM: MM1: Add classic PC speaker sounds
30eb8f2e15 MM: MM1: Update classic PC speaker playback
f6c38ae6fb MM: MM1: Add classic sound test command
Commit: 601fc79921b3a4a6c34af80bdc561dd9c6f618ea
https://github.com/scummvm/scummvm/commit/601fc79921b3a4a6c34af80bdc561dd9c6f618ea
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-06T21:37:57+10:00
Commit Message:
MM: MM1: Add classic PC speaker sounds
Changed paths:
A engines/mm/shared/classic/pc_speaker.cpp
A engines/mm/shared/classic/pc_speaker.h
engines/mm/mm1/game/combat.cpp
engines/mm/mm1/game/encounter.cpp
engines/mm/mm1/sound.cpp
engines/mm/mm1/sound.h
engines/mm/mm1/views/combat.cpp
engines/mm/mm1/views/interactions/hacker.cpp
engines/mm/mm1/views/interactions/inspectron.cpp
engines/mm/mm1/views/interactions/lord_ironfist.cpp
engines/mm/mm1/views/interactions/prisoners.cpp
engines/mm/mm1/views/interactions/prisoners.h
engines/mm/mm1/views/search.cpp
engines/mm/mm1/views/title.cpp
engines/mm/module.mk
diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 3da28dc6cfa..b12442f46d6 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -900,6 +900,7 @@ void Combat::updateMonsterStatus() {
if (val <= 0) {
_monsterP->_hp = 0;
_monsterP->_status = MONFLAG_DEAD;
+ Sound::sound2(SOUND_9);
} else {
_monsterP->_hp = val;
@@ -1107,6 +1108,7 @@ void Combat::resetDestMonster() {
void Combat::spellFailed() {
g_globals->_combatParty[_currentChar]->_checked = true;
+ Sound::sound(SOUND_2);
SoundMessage msg(10, 2, SpellCasting::spellResultMessage(STRING["spells.failed"]));
msg._delaySeconds = 3;
diff --git a/engines/mm/mm1/game/encounter.cpp b/engines/mm/mm1/game/encounter.cpp
index e6bdc19aaba..91520da80c0 100644
--- a/engines/mm/mm1/game/encounter.cpp
+++ b/engines/mm/mm1/game/encounter.cpp
@@ -25,6 +25,7 @@
#include "mm/mm1/events.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/mm1.h"
+#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
@@ -34,6 +35,8 @@ void Encounter::execute() {
if (!g_globals->_encountersOn)
return;
+ Sound::sound2(SOUND_1);
+
Maps::Map &map = *g_maps->_currentMap;
int comp, maxRand, maxVal;
const Monster *monsterP;
diff --git a/engines/mm/mm1/sound.cpp b/engines/mm/mm1/sound.cpp
index 4a747e2cd54..a846c71e382 100644
--- a/engines/mm/mm1/sound.cpp
+++ b/engines/mm/mm1/sound.cpp
@@ -19,13 +19,117 @@
*
*/
-#include "common/textconsole.h"
+#include "common/system.h"
#include "mm/mm1/sound.h"
#include "mm/mm1/mm1.h"
namespace MM {
namespace MM1 {
+static const uint32 kBiosTimerTickMicros = 54925;
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound0[] = {
+ { 1, 0x0a98 }, { 1, 0x0970 }, { 1, 0x08e8 }, { 1, 0x07ef },
+ { 14, 0x0712 }, { 2, 0x0000 }, { 6, 0x0712 }, { 2, 0x0000 },
+ { 6, 0x0712 }, { 1, 0x0000 }, { 1, 0x0e24 }, { 1, 0x0a98 },
+ { 6, 0x08e8 }, { 2, 0x0000 }, { 4, 0x0a98 }, { 4, 0x0970 },
+ { 4, 0x08e8 }, { 3, 0x07ef }, { 1, 0x0000 }, { 3, 0x0712 },
+ { 1, 0x0000 }, { 2, 0x06ac }, { 1, 0x08e8 }, { 1, 0x0712 },
+ { 8, 0x05f2 }, { 4, 0x06ac }, { 4, 0x0712 }, { 8, 0x06ac },
+ { 4, 0x0712 }, { 2, 0x07ef }, { 1, 0x12e0 }, { 1, 0x0b3a },
+ { 12, 0x0712 }, { 4, 0x06ac }, { 4, 0x0712 }, { 3, 0x07ef },
+ { 1, 0x0000 }, { 3, 0x08e8 }, { 1, 0x0000 }, { 3, 0x0970 },
+ { 1, 0x0000 }, { 14, 0x0a98 }, { 2, 0x0000 }, { 6, 0x0e24 },
+ { 2, 0x0000 }, { 5, 0x0a98 }, { 1, 0x0000 }, { 1, 0x0e24 },
+ { 1, 0x0be4 }, { 3, 0x08e8 }, { 1, 0x0000 }, { 3, 0x0be4 },
+ { 1, 0x0000 }, { 2, 0x08e8 }, { 2, 0x0970 }, { 2, 0x08e8 },
+ { 2, 0x07ef }, { 14, 0x0712 }, { 2, 0x0000 }, { 2, 0x06ac },
+ { 2, 0x0712 }, { 2, 0x07ef }, { 2, 0x08e8 }, { 2, 0x0712 },
+ { 2, 0x07ef }, { 2, 0x08e8 }, { 2, 0x0970 }, { 2, 0x07ef },
+ { 2, 0x08e8 }, { 2, 0x0970 }, { 2, 0x0a98 }, { 2, 0x08e8 },
+ { 2, 0x0970 }, { 2, 0x0a98 }, { 2, 0x0b3a }, { 4, 0x06ac },
+ { 5, 0x0712 }, { 6, 0x07ef }, { 7, 0x08e8 }, { 2, 0x0e24 },
+ { 3, 0x0a98 }, { 32, 0x0868 }, { 4, 0x0000 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound1[] = {
+ { 2, 0x0be4 }, { 4, 0x0000 }, { 1, 0x08e8 }, { 1, 0x0000 },
+ { 8, 0x05f2 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound2[] = {
+ { 2, 0x0be4 }, { 4, 0x0000 }, { 1, 0x0be4 }, { 1, 0x0000 },
+ { 1, 0x0be4 }, { 1, 0x0000 }, { 1, 0x0be4 }, { 1, 0x0000 },
+ { 12, 0x07ef }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound3[] = {
+ { 1, 0x11d1 }, { 1, 0x0be4 }, { 3, 0x0712 }, { 6, 0x0000 },
+ { 2, 0x0712 }, { 1, 0x0000 }, { 12, 0x0712 }, { 6, 0x0000 },
+ { 3, 0x0712 }, { 3, 0x0000 }, { 2, 0x06ac }, { 2, 0x0000 },
+ { 2, 0x0be4 }, { 2, 0x0000 }, { 2, 0x06ac }, { 1, 0x11d1 },
+ { 1, 0x0be4 }, { 3, 0x0712 }, { 6, 0x0000 }, { 2, 0x0712 },
+ { 1, 0x0000 }, { 12, 0x0712 }, { 6, 0x0000 }, { 3, 0x0712 },
+ { 2, 0x0000 }, { 1, 0x17c8 }, { 1, 0x0be4 }, { 2, 0x07ef },
+ { 2, 0x0000 }, { 2, 0x08e8 }, { 2, 0x0000 }, { 2, 0x07ef },
+ { 2, 0x0000 }, { 1, 0x0e24 }, { 1, 0x0be4 }, { 3, 0x08e8 },
+ { 6, 0x0000 }, { 2, 0x0be4 }, { 1, 0x0000 }, { 1, 0x8e84 },
+ { 1, 0x5f1e }, { 1, 0x4742 }, { 1, 0x2f8f }, { 1, 0x23a2 },
+ { 1, 0x17c8 }, { 1, 0x11d1 }, { 1, 0x0e24 }, { 1, 0x0be4 },
+ { 12, 0x08e8 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound4[] = {
+ { 16, 0x06ac }, { 3, 0x0000 }, { 4, 0x0868 }, { 4, 0x08e8 },
+ { 4, 0x0970 }, { 16, 0x08e8 }, { 3, 0x0000 }, { 4, 0x0b3a },
+ { 4, 0x0be4 }, { 4, 0x0b3a }, { 24, 0x0d59 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound5[] = {
+ { 2, 0x8e84 }, { 2, 0x5f1e }, { 2, 0x4742 }, { 2, 0x2f8f },
+ { 2, 0x23a2 }, { 2, 0x17c8 }, { 2, 0x11d1 }, { 2, 0x0be4 },
+ { 2, 0x08e8 }, { 2, 0x05f2 }, { 2, 0x0474 }, { 2, 0x0389 },
+ { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound6[] = {
+ { 6, 0x03e8 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound7[] = {
+ { 8, 0x4e20 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound8[] = {
+ { 8, 0x0b3a }, { 8, 0x0be4 }, { 8, 0x0c98 }, { 8, 0x0be4 },
+ { 2, 0x0000 }, { 16, 0x0d59 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound9[] = {
+ { 5, 0x08e8 }, { 1, 0x0000 }, { 1, 0x07ef }, { 1, 0x0000 },
+ { 1, 0x0be4 }, { 1, 0x0000 }, { 1, 0x07ef }, { 1, 0x0000 },
+ { 12, 0x0712 }, { 0, 0x0000 }
+};
+
+Sound::Sound(Audio::Mixer *mixer) : Shared::Xeen::Sound(mixer) {
+ _speaker.init();
+}
+
+void Sound::playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append, bool restart) {
+ if (!g_engine->_sound || !g_engine->_sound->_fxOn)
+ return;
+ if (g_engine->isEnhanced())
+ return;
+
+ if (restart)
+ g_engine->_sound->_speaker.stop();
+
+ if (!append && !restart && g_engine->_sound->_speaker.isPlaying())
+ return;
+
+ g_engine->_sound->_speaker.playPitSequence(sequence, kBiosTimerTickMicros, append);
+}
+
void Sound::sound(SoundId soundNum) {
if (g_engine->isEnhanced()) {
if (soundNum == SOUND_1) {
@@ -34,15 +138,60 @@ void Sound::sound(SoundId soundNum) {
}
}
- warning("TODO: sound %d", (int)soundNum);
+ if (!g_engine->_sound || !g_engine->_sound->_fxOn)
+ return;
+
+ switch (soundNum) {
+ case SOUND_1:
+ playSequence(kMM1Sound7, false, true);
+ break;
+ case SOUND_2:
+ playSequence(kMM1Sound6, false, true);
+ break;
+ case SOUND_3:
+ playSequence(kMM1Sound6, false, true);
+ playSequence(kMM1Sound6, true);
+ playSequence(kMM1Sound6, true);
+ break;
+ default:
+ break;
+ }
}
void Sound::sound2(SoundId soundNum) {
- warning("TODO: sound2 %d", (int)soundNum);
+ switch (soundNum) {
+ case SOUND_TITLE:
+ playSequence(kMM1Sound0, true);
+ break;
+ case SOUND_1:
+ playSequence(kMM1Sound1, true);
+ break;
+ case SOUND_2:
+ playSequence(kMM1Sound2, true);
+ break;
+ case SOUND_3:
+ playSequence(kMM1Sound3, true);
+ break;
+ case SOUND_4:
+ playSequence(kMM1Sound4, true);
+ break;
+ case SOUND_5:
+ playSequence(kMM1Sound5, true);
+ break;
+ case SOUND_8:
+ playSequence(kMM1Sound8, true);
+ break;
+ case SOUND_9:
+ playSequence(kMM1Sound9);
+ break;
+ default:
+ break;
+ }
}
void Sound::stopSound() {
- warning("TODO: stopSound");
+ if (g_engine->_sound)
+ g_engine->_sound->_speaker.stop();
}
} // namespace MM1
diff --git a/engines/mm/mm1/sound.h b/engines/mm/mm1/sound.h
index fb4c306085a..59e924ae728 100644
--- a/engines/mm/mm1/sound.h
+++ b/engines/mm/mm1/sound.h
@@ -23,18 +23,26 @@
#define MM_MM1_SOUND_H
#include "mm/shared/xeen/sound.h"
+#include "mm/shared/classic/pc_speaker.h"
namespace MM {
namespace MM1 {
enum SoundId {
+ SOUND_TITLE = 0,
SOUND_1 = 1, SOUND_2 = 2, SOUND_3 = 3, SOUND_4 = 4,
SOUND_5 = 5, SOUND_8 = 8, SOUND_9 = 9
};
class Sound : public Shared::Xeen::Sound {
+private:
+ Shared::Classic::PcSpeaker _speaker;
+
+ static void playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append = false, bool restart = false);
+
public:
- Sound(Audio::Mixer *mixer) : Shared::Xeen::Sound(mixer) {}
+ Sound(Audio::Mixer *mixer);
+ ~Sound() override {}
static void sound(SoundId soundNum);
static void sound2(SoundId soundNum);
diff --git a/engines/mm/mm1/views/combat.cpp b/engines/mm/mm1/views/combat.cpp
index b753a6968d2..9f1f5388897 100644
--- a/engines/mm/mm1/views/combat.cpp
+++ b/engines/mm/mm1/views/combat.cpp
@@ -91,6 +91,7 @@ bool Combat::msgGame(const GameMessage &msg) {
_spellResult._lines.clear();
_spellResult._lines.push_back(Line(msg._value, 1, msg._stringValue));
_spellResult._delaySeconds = 3;
+ Sound::sound(SOUND_2);
setMode(SPELL_RESULT);
return true;
diff --git a/engines/mm/mm1/views/interactions/hacker.cpp b/engines/mm/mm1/views/interactions/hacker.cpp
index a8fe1310947..5cf257dd9b0 100644
--- a/engines/mm/mm1/views/interactions/hacker.cpp
+++ b/engines/mm/mm1/views/interactions/hacker.cpp
@@ -43,6 +43,7 @@ bool Hacker::msgGame(const GameMessage &msg) {
if (_canAccept) {
// Show the view
Sound::sound(SOUND_2);
+ Sound::sound2(SOUND_2);
addView();
} else {
@@ -58,6 +59,7 @@ bool Hacker::msgGame(const GameMessage &msg) {
g_maps->_mapPos.x--;
map.redrawGame();
+ Sound::sound2(SOUND_2);
send(SoundMessage(
0, 1, STRING["maps.map36.hacker1"],
0, 2, line
diff --git a/engines/mm/mm1/views/interactions/inspectron.cpp b/engines/mm/mm1/views/interactions/inspectron.cpp
index 4abe339748e..d18251f51e3 100644
--- a/engines/mm/mm1/views/interactions/inspectron.cpp
+++ b/engines/mm/mm1/views/interactions/inspectron.cpp
@@ -46,6 +46,7 @@ bool Inspectron::msgGame(const GameMessage &msg) {
if (_canAccept) {
// Open the view for display
Sound::sound(SOUND_2);
+ Sound::sound2(SOUND_2);
addView();
} else {
@@ -63,6 +64,7 @@ bool Inspectron::msgGame(const GameMessage &msg) {
g_maps->_mapPos.y++;
map.redrawGame();
+ Sound::sound2(SOUND_2);
send(SoundMessage(
0, 1, STRING["maps.map35.inspectron1"],
0, 2, line
diff --git a/engines/mm/mm1/views/interactions/lord_ironfist.cpp b/engines/mm/mm1/views/interactions/lord_ironfist.cpp
index 987da0f36ed..427f07cb957 100644
--- a/engines/mm/mm1/views/interactions/lord_ironfist.cpp
+++ b/engines/mm/mm1/views/interactions/lord_ironfist.cpp
@@ -37,6 +37,7 @@ bool LordIronfist::msgFocus(const FocusMessage &msg) {
g_globals->_currCharacter = &g_globals->_party[0];
_canAccept = !g_globals->_currCharacter->_quest;
Sound::sound(SOUND_2);
+ Sound::sound2(SOUND_2);
return TextView::msgFocus(msg);
}
@@ -46,7 +47,6 @@ void LordIronfist::draw() {
clearSurface();
if (_canAccept) {
- Sound::sound2(SOUND_2);
writeString(0, 1, STRING["maps.map43.ironfist1"]);
writeString(0, 2, STRING["maps.map43.ironfist2"]);
diff --git a/engines/mm/mm1/views/interactions/prisoners.cpp b/engines/mm/mm1/views/interactions/prisoners.cpp
index b2e079b7e5b..3d7b30eed17 100644
--- a/engines/mm/mm1/views/interactions/prisoners.cpp
+++ b/engines/mm/mm1/views/interactions/prisoners.cpp
@@ -46,6 +46,11 @@ void Prisoner::draw() {
writeString(STRING["maps.prisoners.options3"]);
}
+bool Prisoner::msgFocus(const FocusMessage &msg) {
+ Sound::sound2(SOUND_2);
+ return TextView::msgFocus(msg);
+}
+
bool Prisoner::msgKeypress(const KeypressMessage &msg) {
if (endDelay())
return true;
diff --git a/engines/mm/mm1/views/interactions/prisoners.h b/engines/mm/mm1/views/interactions/prisoners.h
index 63fbe26abf1..b037fff3eea 100644
--- a/engines/mm/mm1/views/interactions/prisoners.h
+++ b/engines/mm/mm1/views/interactions/prisoners.h
@@ -44,6 +44,7 @@ public:
virtual ~Prisoner() {}
void draw() override;
+ bool msgFocus(const FocusMessage &msg) override;
bool msgKeypress(const KeypressMessage &msg) override;
void timeout() override;
};
diff --git a/engines/mm/mm1/views/search.cpp b/engines/mm/mm1/views/search.cpp
index 19946c44922..21ada2243e5 100644
--- a/engines/mm/mm1/views/search.cpp
+++ b/engines/mm/mm1/views/search.cpp
@@ -421,6 +421,7 @@ void Search::drawItem() {
c._name,
item->_name.c_str()
));
+ Sound::sound2(SOUND_5);
if (treasure.hasItems()) {
delaySeconds(2);
diff --git a/engines/mm/mm1/views/title.cpp b/engines/mm/mm1/views/title.cpp
index 39211cef572..dd9be716574 100644
--- a/engines/mm/mm1/views/title.cpp
+++ b/engines/mm/mm1/views/title.cpp
@@ -68,10 +68,16 @@ bool Title::msgFocus(const FocusMessage &msg) {
_screenNum = -1;
_fadeIndex = 0;
+ if (!g_engine->isEnhanced())
+ Sound::sound2(SOUND_TITLE);
+
return true;
}
bool Title::msgUnfocus(const UnfocusMessage & msg) {
+ if (!g_engine->isEnhanced())
+ Sound::stopSound();
+
for (int i = 0; i < SCREENS_COUNT; ++i)
_screens[i].clear();
diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index 5f69cbaaaf4..cc6c4b28fd4 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS := \
metaengine.o \
mm.o \
shared/utils/bitmap_font.o \
+ shared/classic/pc_speaker.o \
shared/utils/strings.o \
shared/utils/strings_data.o \
shared/utils/xeen_font.o \
diff --git a/engines/mm/shared/classic/pc_speaker.cpp b/engines/mm/shared/classic/pc_speaker.cpp
new file mode 100644
index 00000000000..1834be138ec
--- /dev/null
+++ b/engines/mm/shared/classic/pc_speaker.cpp
@@ -0,0 +1,101 @@
+/* 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/softsynth/pcspk.h"
+#include "mm/shared/classic/pc_speaker.h"
+
+namespace MM {
+namespace Shared {
+namespace Classic {
+
+static const float kPitBaseFrequency = 1193182.0f;
+
+PcSpeaker::PcSpeaker() {
+ _speaker = new Audio::PCSpeaker();
+}
+
+PcSpeaker::~PcSpeaker() {
+ delete _speaker;
+}
+
+bool PcSpeaker::init() {
+ _ready = _speaker->init();
+ return _ready;
+}
+
+void PcSpeaker::stop() {
+ if (_ready)
+ _speaker->stop();
+}
+
+bool PcSpeaker::isPlaying() const {
+ return _ready && _speaker->isPlaying();
+}
+
+void PcSpeaker::playTone(int frequency, int32 lengthMs) {
+ if (_ready)
+ _speaker->play(Audio::PCSpeaker::kWaveFormSquare, frequency, lengthMs);
+}
+
+void PcSpeaker::queueTone(float frequency, uint32 lengthUs) {
+ if (_ready)
+ _speaker->playQueue(Audio::PCSpeaker::kWaveFormSquare, frequency, lengthUs);
+}
+
+void PcSpeaker::queueSilence(uint32 lengthUs) {
+ if (_ready)
+ _speaker->playQueue(Audio::PCSpeaker::kWaveFormSilence, 0.0f, lengthUs);
+}
+
+void PcSpeaker::playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append) {
+ if (!_ready || !sequence)
+ return;
+
+ if (!append)
+ stop();
+
+ for (const PitSequenceEntry *entry = sequence; entry->durationTicks != 0; ++entry) {
+ const uint32 lengthUs = entry->durationTicks * tickMicros;
+ if (entry->pitDivisor == 0) {
+ queueSilence(lengthUs);
+ } else {
+ queueTone(kPitBaseFrequency / entry->pitDivisor, lengthUs);
+ }
+ }
+}
+
+void PcSpeaker::playFrequencySequence(const FrequencySequenceEntry *sequence) {
+ if (!_ready || !sequence)
+ return;
+
+ stop();
+
+ for (const FrequencySequenceEntry *entry = sequence; entry->lengthUs != 0; ++entry) {
+ if (entry->frequency == 0.0f)
+ queueSilence(entry->lengthUs);
+ else
+ queueTone(entry->frequency, entry->lengthUs);
+ }
+}
+
+} // namespace Classic
+} // namespace Shared
+} // namespace MM
diff --git a/engines/mm/shared/classic/pc_speaker.h b/engines/mm/shared/classic/pc_speaker.h
new file mode 100644
index 00000000000..6f7f89f20f1
--- /dev/null
+++ b/engines/mm/shared/classic/pc_speaker.h
@@ -0,0 +1,69 @@
+/* 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 MM_SHARED_CLASSIC_PC_SPEAKER_H
+#define MM_SHARED_CLASSIC_PC_SPEAKER_H
+
+#include "common/scummsys.h"
+
+namespace Audio {
+class PCSpeaker;
+}
+
+namespace MM {
+namespace Shared {
+namespace Classic {
+
+struct PitSequenceEntry {
+ uint16 durationTicks;
+ uint16 pitDivisor;
+};
+
+struct FrequencySequenceEntry {
+ uint32 lengthUs;
+ float frequency;
+};
+
+class PcSpeaker {
+private:
+ Audio::PCSpeaker *_speaker = nullptr;
+ bool _ready = false;
+
+public:
+ PcSpeaker();
+ ~PcSpeaker();
+
+ bool init();
+ void stop();
+ bool isPlaying() const;
+
+ void playTone(int frequency, int32 lengthMs);
+ void queueTone(float frequency, uint32 lengthUs);
+ void queueSilence(uint32 lengthUs);
+ void playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append = false);
+ void playFrequencySequence(const FrequencySequenceEntry *sequence);
+};
+
+} // namespace Classic
+} // namespace Shared
+} // namespace MM
+
+#endif
Commit: 30eb8f2e15b229fb21fe8056748ac59ce7a8cb98
https://github.com/scummvm/scummvm/commit/30eb8f2e15b229fb21fe8056748ac59ce7a8cb98
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-06T21:37:57+10:00
Commit Message:
MM: MM1: Update classic PC speaker playback
Changed paths:
engines/mm/mm1/events.cpp
engines/mm/mm1/sound.cpp
engines/mm/mm1/sound.h
engines/mm/shared/classic/pc_speaker.cpp
engines/mm/shared/classic/pc_speaker.h
diff --git a/engines/mm/mm1/events.cpp b/engines/mm/mm1/events.cpp
index 197e6823d54..91ffb1a673f 100644
--- a/engines/mm/mm1/events.cpp
+++ b/engines/mm/mm1/events.cpp
@@ -24,6 +24,7 @@
#include "mm/mm1/events.h"
#include "mm/mm1/mm1.h"
#include "mm/mm1/gfx/gfx.h"
+#include "mm/mm1/sound.h"
#include "mm/mm1/views/dialogs.h"
#include "mm/mm1/views_enh/dialogs.h"
@@ -74,6 +75,7 @@ void Events::runGame() {
if ((currTime = g_system->getMillis()) >= nextFrameTime) {
nextFrameTime = currTime + FRAME_DELAY;
tick();
+ Sound::update();
drawElements();
_screen->update();
}
diff --git a/engines/mm/mm1/sound.cpp b/engines/mm/mm1/sound.cpp
index a846c71e382..1e50695144f 100644
--- a/engines/mm/mm1/sound.cpp
+++ b/engines/mm/mm1/sound.cpp
@@ -115,7 +115,7 @@ Sound::Sound(Audio::Mixer *mixer) : Shared::Xeen::Sound(mixer) {
_speaker.init();
}
-void Sound::playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append, bool restart) {
+void Sound::playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append, bool restart, bool loop) {
if (!g_engine->_sound || !g_engine->_sound->_fxOn)
return;
if (g_engine->isEnhanced())
@@ -127,7 +127,7 @@ void Sound::playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool
if (!append && !restart && g_engine->_sound->_speaker.isPlaying())
return;
- g_engine->_sound->_speaker.playPitSequence(sequence, kBiosTimerTickMicros, append);
+ g_engine->_sound->_speaker.playPitSequence(sequence, kBiosTimerTickMicros, append, loop);
}
void Sound::sound(SoundId soundNum) {
@@ -161,10 +161,10 @@ void Sound::sound(SoundId soundNum) {
void Sound::sound2(SoundId soundNum) {
switch (soundNum) {
case SOUND_TITLE:
- playSequence(kMM1Sound0, true);
+ playSequence(kMM1Sound0, false, false, true);
break;
case SOUND_1:
- playSequence(kMM1Sound1, true);
+ playSequence(kMM1Sound1, false, true);
break;
case SOUND_2:
playSequence(kMM1Sound2, true);
@@ -189,9 +189,15 @@ void Sound::sound2(SoundId soundNum) {
}
}
-void Sound::stopSound() {
+void Sound::update() {
if (g_engine->_sound)
+ g_engine->_sound->_speaker.update();
+}
+
+void Sound::stopSound() {
+ if (g_engine->_sound) {
g_engine->_sound->_speaker.stop();
+ }
}
} // namespace MM1
diff --git a/engines/mm/mm1/sound.h b/engines/mm/mm1/sound.h
index 59e924ae728..e03decdc8fd 100644
--- a/engines/mm/mm1/sound.h
+++ b/engines/mm/mm1/sound.h
@@ -38,7 +38,7 @@ class Sound : public Shared::Xeen::Sound {
private:
Shared::Classic::PcSpeaker _speaker;
- static void playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append = false, bool restart = false);
+ static void playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append = false, bool restart = false, bool loop = false);
public:
Sound(Audio::Mixer *mixer);
@@ -46,6 +46,7 @@ public:
static void sound(SoundId soundNum);
static void sound2(SoundId soundNum);
+ static void update();
static void stopSound();
};
diff --git a/engines/mm/shared/classic/pc_speaker.cpp b/engines/mm/shared/classic/pc_speaker.cpp
index 1834be138ec..018c9232f59 100644
--- a/engines/mm/shared/classic/pc_speaker.cpp
+++ b/engines/mm/shared/classic/pc_speaker.cpp
@@ -42,6 +42,9 @@ bool PcSpeaker::init() {
}
void PcSpeaker::stop() {
+ _loopingPitSequence = nullptr;
+ _loopingPitTickMicros = 0;
+
if (_ready)
_speaker->stop();
}
@@ -65,13 +68,7 @@ void PcSpeaker::queueSilence(uint32 lengthUs) {
_speaker->playQueue(Audio::PCSpeaker::kWaveFormSilence, 0.0f, lengthUs);
}
-void PcSpeaker::playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append) {
- if (!_ready || !sequence)
- return;
-
- if (!append)
- stop();
-
+void PcSpeaker::queuePitSequence(const PitSequenceEntry *sequence, uint32 tickMicros) {
for (const PitSequenceEntry *entry = sequence; entry->durationTicks != 0; ++entry) {
const uint32 lengthUs = entry->durationTicks * tickMicros;
if (entry->pitDivisor == 0) {
@@ -82,6 +79,18 @@ void PcSpeaker::playPitSequence(const PitSequenceEntry *sequence, uint32 tickMic
}
}
+void PcSpeaker::playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append, bool loop) {
+ if (!_ready || !sequence)
+ return;
+
+ if (!append)
+ stop();
+
+ _loopingPitSequence = loop ? sequence : nullptr;
+ _loopingPitTickMicros = loop ? tickMicros : 0;
+ queuePitSequence(sequence, tickMicros);
+}
+
void PcSpeaker::playFrequencySequence(const FrequencySequenceEntry *sequence) {
if (!_ready || !sequence)
return;
@@ -96,6 +105,11 @@ void PcSpeaker::playFrequencySequence(const FrequencySequenceEntry *sequence) {
}
}
+void PcSpeaker::update() {
+ if (_loopingPitSequence && !isPlaying())
+ queuePitSequence(_loopingPitSequence, _loopingPitTickMicros);
+}
+
} // namespace Classic
} // namespace Shared
} // namespace MM
diff --git a/engines/mm/shared/classic/pc_speaker.h b/engines/mm/shared/classic/pc_speaker.h
index 6f7f89f20f1..9a558d701f6 100644
--- a/engines/mm/shared/classic/pc_speaker.h
+++ b/engines/mm/shared/classic/pc_speaker.h
@@ -45,8 +45,12 @@ struct FrequencySequenceEntry {
class PcSpeaker {
private:
Audio::PCSpeaker *_speaker = nullptr;
+ const PitSequenceEntry *_loopingPitSequence = nullptr;
+ uint32 _loopingPitTickMicros = 0;
bool _ready = false;
+ void queuePitSequence(const PitSequenceEntry *sequence, uint32 tickMicros);
+
public:
PcSpeaker();
~PcSpeaker();
@@ -58,8 +62,9 @@ public:
void playTone(int frequency, int32 lengthMs);
void queueTone(float frequency, uint32 lengthUs);
void queueSilence(uint32 lengthUs);
- void playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append = false);
+ void playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append = false, bool loop = false);
void playFrequencySequence(const FrequencySequenceEntry *sequence);
+ void update();
};
} // namespace Classic
Commit: f6c38ae6fbf0110e20e4e6eb6a867f767a9e1000
https://github.com/scummvm/scummvm/commit/f6c38ae6fbf0110e20e4e6eb6a867f767a9e1000
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-06T21:37:57+10:00
Commit Message:
MM: MM1: Add classic sound test command
Changed paths:
engines/mm/mm1/console.cpp
engines/mm/mm1/console.h
diff --git a/engines/mm/mm1/console.cpp b/engines/mm/mm1/console.cpp
index 78e36c45977..958f1999027 100644
--- a/engines/mm/mm1/console.cpp
+++ b/engines/mm/mm1/console.cpp
@@ -22,15 +22,371 @@
#include "common/file.h"
#include "common/savefile.h"
#include "common/system.h"
+#include "common/util.h"
#include "mm/shared/utils/strings.h"
#include "mm/mm1/console.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/events.h"
#include "mm/mm1/game/spells_party.h"
+#include "mm/mm1/messages.h"
+#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
+namespace {
+
+const char *const SOUND_TESTS[] = {
+ "wall",
+ "message",
+ "triple",
+ "encounter",
+ "combat",
+ "search",
+ "cast",
+ "death"
+};
+
+const SoundId SOUND_TEST_SOUND_IDS[] = {
+ SOUND_1, SOUND_2, SOUND_3
+};
+
+const SoundId SOUND_TEST_SOUND2_IDS[] = {
+ SOUND_TITLE, SOUND_1, SOUND_2, SOUND_3, SOUND_4, SOUND_5, SOUND_8, SOUND_9
+};
+
+class SoundTestRunner;
+SoundTestRunner *g_soundTestRunner = nullptr;
+
+void faceDirection(Maps::DirMask dir) {
+ Maps::Maps &maps = g_globals->_maps;
+
+ for (int i = 0; i < 4 && maps._forwardMask != dir; ++i)
+ maps.turnLeft();
+}
+
+bool stageWallBump() {
+ Maps::Maps &maps = g_globals->_maps;
+ maps.loadTown(Maps::SORPIGAL);
+ g_globals->_intangible = false;
+
+ static const Maps::DirMask DIRS[] = {
+ Maps::DIRMASK_N, Maps::DIRMASK_E, Maps::DIRMASK_S, Maps::DIRMASK_W
+ };
+
+ for (int y = 0; y < MAP_H; ++y) {
+ for (int x = 0; x < MAP_W; ++x) {
+ const uint offset = y * MAP_W + x;
+ if (maps._currentMap->_states[offset] & Maps::CELL_SPECIAL)
+ continue;
+
+ for (uint i = 0; i < ARRAYSIZE(DIRS); ++i) {
+ const Maps::DirMask dir = DIRS[i];
+ if (!(maps._currentMap->_walls[offset] & dir) ||
+ !(maps._currentMap->_states[offset] & 0x55 & dir))
+ continue;
+
+ maps._mapPos = Common::Point(x, y);
+ faceDirection(dir);
+ maps._mapOffset = offset;
+ maps._currentWalls = maps._currentMap->_walls[offset];
+ maps._currentState = maps._currentMap->_states[offset];
+ g_events->send("View", ActionMessage(KEYBIND_FORWARDS));
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+Character *findSpellTester() {
+ for (uint i = 0; i < g_globals->_party.size(); ++i) {
+ Character &c = g_globals->_party[i];
+ if (c._class == CLERIC || c._class == SORCERER || c._class == ARCHER)
+ return &c;
+ }
+
+ return g_globals->_party.empty() ? nullptr : &g_globals->_party[0];
+}
+
+void stageSpellDone() {
+ Character *c = findSpellTester();
+ if (!c)
+ return;
+
+ g_globals->_currCharacter = c;
+ c->_class = SORCERER;
+ c->_spellLevel = 7;
+ c->_sp = 100;
+ c->_gems = 100;
+
+ int spellIndex = Console::getSpellIndex(c, 1, 5);
+ g_events->send("CastSpell", GameMessage("SPELL", spellIndex));
+}
+
+void stageSearchTreasure() {
+ g_globals->_treasure.clear();
+ g_globals->_treasure._container = CLOTH_SACK;
+ g_globals->_treasure._trapType = 2;
+ g_globals->_treasure.setGold(6);
+ g_globals->_treasure.setGems(0);
+ g_events->send("Search", GameMessage("SHOW"));
+}
+
+void stageEncounter() {
+ if (g_globals->_party.empty()) {
+ warning("MM1 sound test: encounter requires an active party");
+ return;
+ }
+
+ Game::Encounter &enc = g_globals->_encounters;
+ const bool encountersOn = g_globals->_encountersOn;
+ g_globals->_encountersOn = true;
+
+ enc.clearMonsters();
+ enc.addMonster(1, 1);
+ enc._manual = true;
+ enc._levelIndex = 80;
+ enc._encounterType = Game::FORCE_SURPRISED;
+ enc.execute();
+
+ g_globals->_encountersOn = encountersOn;
+}
+
+void stageTripleBeepSpecial() {
+ g_events->send("GameMessages", InfoMessage(
+ 0, 1, STRING["maps.map13.snake_pit"],
+ 0, 2, STRING["maps.map13.levitation2"]
+ ));
+ Sound::sound(SOUND_3);
+}
+
+void stageCombatKill() {
+ Character *c = g_globals->_party.empty() ? nullptr : &g_globals->_party[0];
+ if (!c)
+ return;
+
+ c->_class = KNIGHT;
+ c->_condition = FINE;
+ c->_level = 50;
+ c->_hp = c->_hpCurrent = c->_hpMax = 200;
+ c->_might = 50;
+ c->_accuracy = 50;
+ c->_speed = 50;
+ c->_physicalAttr = 50;
+ g_globals->_currCharacter = c;
+
+ stageEncounter();
+ g_events->send("Combat", GameMessage("COMBAT"));
+}
+
+bool runSoundTestCase(const Common::String &testName) {
+ if (!scumm_stricmp(testName.c_str(), "wall")) {
+ return stageWallBump();
+
+ } else if (!scumm_stricmp(testName.c_str(), "message")) {
+ g_events->send("GameMessages", SoundMessage(STRING["dialogs.search.nothing"]));
+ return true;
+
+ } else if (!scumm_stricmp(testName.c_str(), "triple")) {
+ stageTripleBeepSpecial();
+ return true;
+
+ } else if (!scumm_stricmp(testName.c_str(), "encounter")) {
+ stageEncounter();
+ return true;
+
+ } else if (!scumm_stricmp(testName.c_str(), "combat")) {
+ stageCombatKill();
+ return true;
+
+ } else if (!scumm_stricmp(testName.c_str(), "search")) {
+ stageSearchTreasure();
+ return true;
+
+ } else if (!scumm_stricmp(testName.c_str(), "death") ||
+ !scumm_stricmp(testName.c_str(), "dead")) {
+ g_events->addView("Dead");
+ return true;
+
+ } else if (!scumm_stricmp(testName.c_str(), "cast")) {
+ stageSpellDone();
+ return true;
+ }
+
+ return false;
+}
+
+bool parseSoundId(const char *str, SoundId &soundId) {
+ char *end = nullptr;
+ const long value = strtol(str, &end, 10);
+
+ if (!str[0] || *end)
+ return false;
+
+ switch (value) {
+ case SOUND_TITLE:
+ case SOUND_1:
+ case SOUND_2:
+ case SOUND_3:
+ case SOUND_4:
+ case SOUND_5:
+ case SOUND_8:
+ case SOUND_9:
+ soundId = (SoundId)value;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isSoundIdSupported(const SoundId *ids, uint count, SoundId soundId) {
+ for (uint i = 0; i < count; ++i) {
+ if (ids[i] == soundId)
+ return true;
+ }
+
+ return false;
+}
+
+bool runDirectSoundTest(const Common::String &routine, SoundId soundId) {
+ if (!scumm_stricmp(routine.c_str(), "sound")) {
+ if (!isSoundIdSupported(SOUND_TEST_SOUND_IDS, ARRAYSIZE(SOUND_TEST_SOUND_IDS), soundId))
+ return false;
+
+ Sound::sound(soundId);
+ return true;
+ }
+
+ if (!scumm_stricmp(routine.c_str(), "sound2")) {
+ if (!isSoundIdSupported(SOUND_TEST_SOUND2_IDS, ARRAYSIZE(SOUND_TEST_SOUND2_IDS), soundId))
+ return false;
+
+ Sound::sound2(soundId);
+ return true;
+ }
+
+ return false;
+}
+
+bool runDirectSoundTestAlias(const Common::String &testName) {
+ const char *name = testName.c_str();
+ SoundId soundId;
+
+ if (!strncmp(name, "sound2-", 7) && parseSoundId(name + 7, soundId))
+ return runDirectSoundTest("sound2", soundId);
+
+ if (!strncmp(name, "sound-", 6) && parseSoundId(name + 6, soundId))
+ return runDirectSoundTest("sound", soundId);
+
+ return false;
+}
+
+void soundTestPromptAccepted();
+
+class SoundTestRunner : public UIElement {
+private:
+ enum State {
+ ST_INACTIVE,
+ ST_PROMPT,
+ ST_RUN,
+ ST_WAIT
+ };
+
+ State _state = ST_INACTIVE;
+ uint _testIndex = 0;
+ uint32 _waitUntil = 0;
+ bool _promptOpen = false;
+ bool _promptAccepted = false;
+
+ void nextTest() {
+ ++_testIndex;
+ _promptOpen = false;
+ _promptAccepted = false;
+
+ if (_testIndex >= ARRAYSIZE(SOUND_TESTS)) {
+ _state = ST_INACTIVE;
+ close();
+ } else {
+ _state = ST_PROMPT;
+ }
+ }
+
+ uint32 waitMillisForTest(const Common::String &testName) const {
+ if (!scumm_stricmp(testName.c_str(), "encounter"))
+ return 5000;
+ if (!scumm_stricmp(testName.c_str(), "combat"))
+ return 8000;
+ if (!scumm_stricmp(testName.c_str(), "cast"))
+ return 3000;
+ if (!scumm_stricmp(testName.c_str(), "search"))
+ return 3000;
+ return 2000;
+ }
+
+public:
+ SoundTestRunner(UIElement *owner) : UIElement("SoundTestRunner", owner) {}
+
+ void start() {
+ _state = ST_PROMPT;
+ _testIndex = 0;
+ _promptOpen = false;
+ _promptAccepted = false;
+ _waitUntil = 0;
+ }
+
+ void promptAccepted() {
+ _promptAccepted = true;
+ }
+
+ bool tick() override {
+ if (_state == ST_INACTIVE)
+ return false;
+
+ const Common::String testName = SOUND_TESTS[_testIndex];
+
+ switch (_state) {
+ case ST_PROMPT:
+ if (!_promptOpen) {
+ _promptOpen = true;
+ g_events->send("GameMessages", InfoMessage(
+ 0, 3,
+ Common::String::format("proceed with %s test", testName.c_str()),
+ soundTestPromptAccepted));
+ return true;
+ }
+
+ if (_promptAccepted)
+ _state = ST_RUN;
+ return true;
+
+ case ST_RUN:
+ runSoundTestCase(testName);
+ _waitUntil = g_system->getMillis() + waitMillisForTest(testName);
+ _state = ST_WAIT;
+ return true;
+
+ case ST_WAIT:
+ if (g_system->getMillis() >= _waitUntil)
+ nextTest();
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+};
+
+void soundTestPromptAccepted() {
+ if (g_soundTestRunner)
+ g_soundTestRunner->promptAccepted();
+}
+
+} // namespace
+
Console::Console() : GUI::Debugger() {
registerCmd("dump_map", WRAP_METHOD(Console, cmdDumpMap));
registerCmd("dump_monsters", WRAP_METHOD(Console, cmdDumpMonsters));
@@ -47,6 +403,28 @@ Console::Console() : GUI::Debugger() {
registerCmd("specials", WRAP_METHOD(Console, cmdSpecials));
registerCmd("special", WRAP_METHOD(Console, cmdSpecial));
registerCmd("view", WRAP_METHOD(Console, cmdView));
+ registerCmd("sound", WRAP_METHOD(Console, cmdSoundTest));
+}
+
+Console::~Console() {
+ if (g_soundTestRunner == _soundTestRunner)
+ g_soundTestRunner = nullptr;
+ delete _soundTestRunner;
+}
+
+void Console::postEnter() {
+ GUI::Debugger::postEnter();
+
+ if (_pendingSoundTest) {
+ _pendingSoundTest = false;
+ if (!_soundTestRunner) {
+ UIElement *game = g_events->findView("Game");
+ assert(game);
+ _soundTestRunner = new SoundTestRunner(game);
+ g_soundTestRunner = static_cast<SoundTestRunner *>(_soundTestRunner);
+ }
+ static_cast<SoundTestRunner *>(_soundTestRunner)->start();
+ }
}
bool Console::cmdDumpMap(int argc, const char **argv) {
@@ -489,5 +867,49 @@ bool Console::cmdView(int argc, const char **argv) {
}
}
+bool Console::cmdSoundTest(int argc, const char **argv) {
+ Common::String testName;
+ SoundId soundId;
+
+ if (argc > 1 && !scumm_stricmp(argv[1], "list")) {
+ debugPrintf("sound [test|list|wall|message|triple|encounter|combat|search|cast|death]\n");
+ debugPrintf("sound sound <1|2|3>\n");
+ debugPrintf("sound sound2 <0|1|2|3|4|5|8|9>\n");
+ debugPrintf(" sound <name> stages one gameplay event for classic sound testing.\n");
+ debugPrintf(" sound test runs the staged checks interactively.\n");
+ debugPrintf(" sound sound <id> calls the original sound(id) routine directly.\n");
+ debugPrintf(" sound sound2 <id> calls the original sound2(id) routine directly.\n");
+ for (uint i = 0; i < ARRAYSIZE(SOUND_TESTS); ++i)
+ debugPrintf(" %u: %s\n", i + 1, SOUND_TESTS[i]);
+ for (uint i = 0; i < ARRAYSIZE(SOUND_TEST_SOUND_IDS); ++i)
+ debugPrintf(" sound-%d\n", SOUND_TEST_SOUND_IDS[i]);
+ for (uint i = 0; i < ARRAYSIZE(SOUND_TEST_SOUND2_IDS); ++i)
+ debugPrintf(" sound2-%d\n", SOUND_TEST_SOUND2_IDS[i]);
+ return true;
+ }
+
+ if (argc == 1 || !scumm_stricmp(argv[1], "test")) {
+ _pendingSoundTest = true;
+ return false;
+ }
+
+ if (argc == 3 && parseSoundId(argv[2], soundId)) {
+ if (runDirectSoundTest(argv[1], soundId))
+ return false;
+
+ debugPrintf("Sound routine '%s' does not support id %d\n", argv[1], soundId);
+ debugPrintf("Use 'sound list' to show known tests.\n");
+ return true;
+ }
+
+ testName = argv[1];
+ if (runDirectSoundTestAlias(testName) || runSoundTestCase(testName))
+ return false;
+
+ debugPrintf("Unknown sound test '%s'\n", argv[1]);
+ debugPrintf("Use 'sound list' to show known tests.\n");
+ return true;
+}
+
} // namespace MM1
} // namespace MM
diff --git a/engines/mm/mm1/console.h b/engines/mm/mm1/console.h
index 5c180eaff44..05d9a7b7375 100644
--- a/engines/mm/mm1/console.h
+++ b/engines/mm/mm1/console.h
@@ -28,8 +28,16 @@
namespace MM {
namespace MM1 {
+class UIElement;
+
class Console : public GUI::Debugger, public MM1::Game::SpellCasting {
+private:
+ bool _pendingSoundTest = false;
+ UIElement *_soundTestRunner = nullptr;
+
protected:
+ void postEnter() override;
+
/**
* Used to dump a map's code and data
*/
@@ -105,9 +113,14 @@ protected:
*/
bool cmdDumpRoster(int argc, const char **argv);
+ /**
+ * Stages gameplay events used to test classic sounds
+ */
+ bool cmdSoundTest(int argc, const char **argv);
+
public:
Console();
- ~Console() override {}
+ ~Console() override;
};
} // namespace MM1
More information about the Scummvm-git-logs
mailing list