[Scummvm-git-logs] scummvm master -> 03e90922c932bc5ed41eb0e95a9baf6b1e7c8bfa
NMIError
noreply at scummvm.org
Fri Oct 9 16:55:27 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:
d755c2d919 AUDIO/MIDI: Add music_mute / sfx_mute volume scaling
c7fc48767d MADS: Rex MT-32 fixes
140dfaa9ed MADS: More Rex MT-32 refactoring
75aee5d537 MADS: Refactor Rex demo MT-32 code
1acb7ec2a1 MADS: Rex MT-32 music / SFX volume control
03e90922c9 MADS: Fix Rex MT-32 volume fade-out bug
Commit: d755c2d91949c8f27a50f205cf9da8ae47b867e6
https://github.com/scummvm/scummvm/commit/d755c2d91949c8f27a50f205cf9da8ae47b867e6
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-10-09T18:19:49+02:00
Commit Message:
AUDIO/MIDI: Add music_mute / sfx_mute volume scaling
Changed paths:
audio/adlib_ms.cpp
audio/casio.cpp
audio/mididrv_ms.cpp
audio/mididrv_ms.h
audio/miles_adlib.cpp
audio/mt32gm.cpp
diff --git a/audio/adlib_ms.cpp b/audio/adlib_ms.cpp
index d13015b0762..80c512bf442 100644
--- a/audio/adlib_ms.cpp
+++ b/audio/adlib_ms.cpp
@@ -1738,11 +1738,11 @@ uint8 MidiDriver_ADLIB_Multisource::calculateVolume(uint8 channel, uint8 source,
// Scale by source volume.
invertedVolume = (invertedVolume * _sources[source].volume) / _sources[source].neutralVolume;
if (_userVolumeScaling) {
- if (_userMute) {
+ if (_userMute || (_sources[source].type == SOURCE_TYPE_SFX ? _userSfxMute : _userMusicMute)) {
invertedVolume = 0;
} else {
// Scale by user volume.
- uint16 userVolume = (_sources[source].type == SOURCE_TYPE_SFX ? _userSfxVolume : _userMusicVolume); // Treat SOURCE_TYPE_UNDEFINED as music
+ uint16 userVolume = _sources[source].type == SOURCE_TYPE_SFX ? _userSfxVolume : _userMusicVolume; // Treat SOURCE_TYPE_UNDEFINED as music
invertedVolume = (invertedVolume * userVolume) >> 8;
}
}
diff --git a/audio/casio.cpp b/audio/casio.cpp
index e9c7c5ea6f4..0ee8beab6cf 100644
--- a/audio/casio.cpp
+++ b/audio/casio.cpp
@@ -285,11 +285,11 @@ byte MidiDriver_Casio::calculateVelocity(int8 source, byte velocity) {
calculatedVelocity = 0;
}
if (_userVolumeScaling) {
- if (_userMute) {
+ if (_userMute || ((source >= 0 && _sources[source].type == SOURCE_TYPE_SFX) ? _userSfxMute : _userMusicMute)) {
calculatedVelocity = 0;
} else {
// Scale to user volume.
- uint16 userVolume = _sources[source].type == SOURCE_TYPE_SFX ? _userSfxVolume : _userMusicVolume;
+ uint16 userVolume = ((source >= 0 && _sources[source].type == SOURCE_TYPE_SFX) ? _userSfxVolume : _userMusicVolume);
if (userVolume == 0)
calculatedVelocity = 0;
}
diff --git a/audio/mididrv_ms.cpp b/audio/mididrv_ms.cpp
index a214c007883..13cc984baf1 100644
--- a/audio/mididrv_ms.cpp
+++ b/audio/mididrv_ms.cpp
@@ -60,6 +60,8 @@ MidiDriver_Multisource::MidiDriver_Multisource() :
_userMusicVolume(192),
_userSfxVolume(192),
_userMute(false),
+ _userMusicMute(false),
+ _userSfxMute(false),
_timerRate(0),
_fadeDelay(0),
_timer_param(nullptr),
@@ -382,6 +384,8 @@ void MidiDriver_Multisource::syncSoundSettings() {
_userMusicVolume = MIN(256, ConfMan.getInt("music_volume"));
_userSfxVolume = MIN(256, ConfMan.getInt("sfx_volume"));
_userMute = ConfMan.getBool("mute");
+ _userMusicMute = ConfMan.getBool("music_mute");
+ _userSfxMute = ConfMan.getBool("sfx_mute");
// Calling applySourceVolume will apply the user volume.
applySourceVolume(0xFF);
diff --git a/audio/mididrv_ms.h b/audio/mididrv_ms.h
index 07b665df992..a9f8cac5280 100644
--- a/audio/mididrv_ms.h
+++ b/audio/mididrv_ms.h
@@ -467,6 +467,8 @@ protected:
uint16 _userMusicVolume;
uint16 _userSfxVolume;
bool _userMute;
+ bool _userMusicMute;
+ bool _userSfxMute;
Common::Mutex _fadingMutex; // For operations on fades
diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp
index 4cb52c70086..ff383c844a0 100644
--- a/audio/miles_adlib.cpp
+++ b/audio/miles_adlib.cpp
@@ -822,7 +822,7 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
// Scale by source volume.
compositeVolume = compositeVolume * _sources[0].volume / _sources[0].neutralVolume;
if (_userVolumeScaling) {
- if (_userMute) {
+ if (_userMute || (_sources[0].type == SOURCE_TYPE_SFX ? _userSfxMute : _userMusicMute)) {
compositeVolume = 0;
} else {
// Scale by user volume.
diff --git a/audio/mt32gm.cpp b/audio/mt32gm.cpp
index 8ee474f6e47..78887039c74 100644
--- a/audio/mt32gm.cpp
+++ b/audio/mt32gm.cpp
@@ -596,7 +596,7 @@ void MidiDriver_MT32GM::controlChange(byte outputChannel, byte controllerNumber,
controllerValue = (controllerValue * _sources[source].volume) / _sources[source].neutralVolume;
}
if (_userVolumeScaling) {
- if (_userMute) {
+ if (_userMute || ((source >= 0 && _sources[source].type == SOURCE_TYPE_SFX) ? _userSfxMute : _userMusicMute)) {
controllerValue = 0;
} else {
// Scale to user volume
Commit: c7fc48767d2e27641aab2dd4f17976f7da78e177
https://github.com/scummvm/scummvm/commit/c7fc48767d2e27641aab2dd4f17976f7da78e177
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-10-09T18:19:49+02:00
Commit Message:
MADS: Rex MT-32 fixes
- RSound code readability
- User volume settings applied
- SysEx delays awaited during event loop
Changed paths:
engines/mads/core/config.cpp
engines/mads/core/config.h
engines/mads/core/sound_manager.cpp
engines/mads/core/sound_manager.h
engines/mads/mads.cpp
engines/mads/mads.h
engines/mads/nebular/menus.cpp
engines/mads/nebular/sound/rsound.cpp
engines/mads/nebular/sound/rsound.h
engines/mads/nebular/sound/rsound_nebular.cpp
engines/mads/nebular/sound/sound.cpp
diff --git a/engines/mads/core/config.cpp b/engines/mads/core/config.cpp
index 16849f14e31..e070e441341 100644
--- a/engines/mads/core/config.cpp
+++ b/engines/mads/core/config.cpp
@@ -64,9 +64,7 @@ void read_config_file() {
ConfMan.registerDefault("difficulty", -1);
ConfMan.registerDefault("animview_minimum_black_ticks", -1);
- config_file.music_flag = !ConfMan.getBool("music_mute") && !ConfMan.getBool("mute");
- config_file.sound_flag = !ConfMan.getBool("sfx_mute") && !ConfMan.getBool("mute");
- config_file.speech_flag = !ConfMan.getBool("speech_mute") && !ConfMan.getBool("mute");
+ read_sound_settings();
config_file.interface_hotspots = ConfMan.getBool("interface_hotspots") ? INTERFACE_BRAINDEAD : INTERFACE_MACINTOSH;
config_file.inventory_mode = ConfMan.getBool("inventory_mode") ? INVENTORY_SPINNING : INVENTORY_SQUAT;
@@ -88,6 +86,12 @@ void read_config_file() {
game.difficulty = ConfMan.getInt("difficulty");
}
+void read_sound_settings() {
+ config_file.music_flag = !ConfMan.getBool("music_mute") && !ConfMan.getBool("mute");
+ config_file.sound_flag = !ConfMan.getBool("sfx_mute") && !ConfMan.getBool("mute");
+ config_file.speech_flag = !ConfMan.getBool("speech_mute") && !ConfMan.getBool("mute");
+}
+
void write_config_file() {
ConfMan.setBool("music_mute", !config_file.music_flag);
ConfMan.setBool("sfx_mute", !config_file.sound_flag);
diff --git a/engines/mads/core/config.h b/engines/mads/core/config.h
index af92acd6cf0..dd1999a2270 100644
--- a/engines/mads/core/config.h
+++ b/engines/mads/core/config.h
@@ -104,6 +104,7 @@ extern ConfigFile config_file;
extern void init_config();
extern void read_config_file();
+extern void read_sound_settings();
extern void write_config_file();
} // namespace MADS
diff --git a/engines/mads/core/sound_manager.cpp b/engines/mads/core/sound_manager.cpp
index 1517a4f2281..e7f9b977034 100644
--- a/engines/mads/core/sound_manager.cpp
+++ b/engines/mads/core/sound_manager.cpp
@@ -25,6 +25,7 @@
#include "common/file.h"
#include "common/memstream.h"
#include "common/textconsole.h"
+#include "mads/mads.h"
#include "mads/core/sound_manager.h"
namespace Audio {
@@ -93,8 +94,24 @@ void SoundManager::init(int sectionNumber) {
return;
}
- // Set volume for newly loaded driver
- _driver->setVolume(_masterVolume);
+ // Wait for driver to be ready to start playback
+ // (i.e. wait for MT-32 SysExes)
+ while (!isReady()) {
+ g_engine->flushKeys();
+
+ if (g_engine->shouldQuit()) {
+ if (_midiDriver != nullptr)
+ _midiDriver->clearSysExQueue();
+ return;
+ }
+ }
+}
+
+bool SoundManager::isReady() {
+ if (_midiDriver == nullptr)
+ return true;
+
+ return _midiDriver->isReady();
}
bool SoundManager::isDriverActive() {
@@ -128,11 +145,9 @@ void SoundManager::startQueuedCommands() {
}
}
-void SoundManager::setVolume(int volume) {
- _masterVolume = volume;
-
- if (_driver)
- _driver->setVolume(volume);
+void SoundManager::syncSoundSettings() {
+ if (_midiDriver != nullptr)
+ _midiDriver->syncSoundSettings();
}
int SoundManager::command(int commandId, int param) {
@@ -157,6 +172,11 @@ void SoundManager::stop() {
_driver->stop();
}
+void SoundManager::pause(bool pause) {
+ if (_driver != nullptr)
+ _driver->pause(pause);
+}
+
void SoundManager::noise() {
if (_driver)
_driver->noise();
@@ -191,7 +211,7 @@ void SoundManager::timerCallback(void *data) {
//====================================================================
SoundDriver::SoundDriver(Audio::Mixer *mixer, const Common::Path &filename,
- int dataOffset, int dataSize) : _mixer(mixer) {
+ int dataOffset, int dataSize) : _mixer(mixer), _paused(false) {
// Open up the appropriate sound file
Common::File soundFile;
if (!soundFile.open(filename))
@@ -202,4 +222,14 @@ SoundDriver::SoundDriver(Audio::Mixer *mixer, const Common::Path &filename,
soundFile.read(&_soundData[0], dataSize);
}
+void SoundDriver::pause(bool paused) {
+ if (_paused == paused)
+ return;
+
+ _paused = paused;
+
+ if (_paused)
+ stopAllNotes();
+}
+
} // namespace MADS
diff --git a/engines/mads/core/sound_manager.h b/engines/mads/core/sound_manager.h
index f03c36546bb..16680a84d42 100644
--- a/engines/mads/core/sound_manager.h
+++ b/engines/mads/core/sound_manager.h
@@ -42,6 +42,7 @@ protected:
Audio::Mixer *_mixer;
Common::Array<byte> _soundData;
Common::Mutex _driverMutex;
+ bool _paused;
/**
* Gets a stream starting at a given offset in the loaded sound data
@@ -50,7 +51,7 @@ protected:
return Common::MemoryReadStream(&_soundData[offset], _soundData.size() - offset);
}
- explicit SoundDriver(Audio::Mixer *mixer) : _mixer(mixer) {}
+ explicit SoundDriver(Audio::Mixer *mixer) : _mixer(mixer), _paused(false) {}
public:
/**
@@ -77,6 +78,11 @@ public:
*/
virtual int stop() = 0;
+ /**
+ * Pause or unpause sound playback
+ */
+ virtual void pause(bool paused);
+
/**
* Main poll method to allow sounds to progress
*/
@@ -87,10 +93,13 @@ public:
*/
virtual void noise() = 0;
+ // TODO Remove
+ virtual void setVolume(int volume) { };
+
/**
- * Set the volume
+ * Stop all active notes
*/
- virtual void setVolume(int volume) = 0;
+ virtual void stopAllNotes() { }
};
class SoundManager {
@@ -114,7 +123,6 @@ protected:
SoundDriver *_driver = nullptr;
bool _newSoundsPaused = false;
Common::Queue<QueuedCommand> _queuedCommands;
- int _masterVolume = 255;
NativeSoundTimer _hostTimer;
@@ -147,6 +155,13 @@ public:
return _driver != nullptr;
}
+ /**
+ * Sound manager is ready to start sound playback.
+ * This will return false while the MT-32 driver is sending SysEx messages
+ * to the unit. Do not start sound playback while this returns false.
+ */
+ bool isReady();
+
/**
* Returns whether the current driver reports an active sound.
*/
@@ -180,9 +195,9 @@ public:
virtual void startQueuedCommands();
/**
- * Set the master volume
+ * Applies sound settings from the ScummVM configuration manager.
*/
- void setVolume(int volume);
+ void syncSoundSettings();
//@{
/**
@@ -197,6 +212,11 @@ public:
*/
void stop();
+ /**
+ * Pauses or unpauses playback.
+ */
+ void pause(bool pause);
+
/**
* Noise
* Some sort of random noise generation?
diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp
index a0da8e8d368..e3ca98d940a 100644
--- a/engines/mads/mads.cpp
+++ b/engines/mads/mads.cpp
@@ -25,7 +25,6 @@
#include "engines/util.h"
#include "graphics/scaler.h"
#include "mads/mads.h"
-#include "mads/mads.h"
#include "mads/core/anim.h"
#include "mads/core/attr.h"
#include "mads/core/camera.h"
@@ -428,6 +427,13 @@ void MADSEngine::updateDisplay() {
presentScreen(0);
}
+void MADSEngine::pauseEngineIntern(bool pause) {
+ Engine::pauseEngineIntern(pause);
+
+ if (_soundManager != nullptr)
+ _soundManager->pause(pause);
+}
+
uint32 MADSEngine::getMillis() {
pollEvents();
return g_system->getMillis();
@@ -445,6 +451,14 @@ bool MADSEngine::isSpeechPlaying() const {
return _mixer->isSoundHandleActive(_speechHandle);
}
+void MADSEngine::syncSoundSettings() {
+ Engine::syncSoundSettings();
+ read_sound_settings();
+
+ if (_soundManager != nullptr)
+ _soundManager->syncSoundSettings();
+}
+
SaveStateList MADSEngine::listSaves() const {
return getMetaEngine()->listSaves(_targetName.c_str());
}
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index a1301426aaf..20fc018c9f6 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -137,6 +137,8 @@ public:
void warpMouse(int x, int y);
void updateDisplay();
+ void pauseEngineIntern(bool pause) override;
+
const Graphics::Surface &getSavegameThumbnail() const {
return _savegameThumbnail;
}
@@ -209,6 +211,7 @@ public:
void playSpeech(Audio::AudioStream *stream);
void stopSpeech();
bool isSpeechPlaying() const;
+ void syncSoundSettings() override;
/**
* Sets the timer function to call at 60Hz
diff --git a/engines/mads/nebular/menus.cpp b/engines/mads/nebular/menus.cpp
index 6a5e0a1138c..d6c697123fb 100644
--- a/engines/mads/nebular/menus.cpp
+++ b/engines/mads/nebular/menus.cpp
@@ -1060,6 +1060,7 @@ static void game_menu_options() {
save_config = config_file;
} else {
write_config_file();
+ g_engine->syncSoundSettings();
}
}
diff --git a/engines/mads/nebular/sound/rsound.cpp b/engines/mads/nebular/sound/rsound.cpp
index 9bcf5bb16df..8672b4a0cc2 100644
--- a/engines/mads/nebular/sound/rsound.cpp
+++ b/engines/mads/nebular/sound/rsound.cpp
@@ -37,8 +37,8 @@ void Channel::loadData(byte *soundData) {
_noteDurationOffset = 0;
_fadeOutActive = 0;
_volumeFadeSpeed = 0xFF;
- _pitchBend = 64;
- _panning = 64;
+ _pitchBend = 0x40;
+ _panning = 0x40;
_soundDataStart = soundData;
_pSrc = soundData;
_innerLoopStart = soundData;
@@ -47,7 +47,7 @@ void Channel::loadData(byte *soundData) {
}
void Channel::setFadeOut(bool fadeOut) {
- if (_deltaCounter) {
+ if (_deltaCounter > 0) {
_fadeOutActive = fadeOut;
// WORKAROUND: matches the same apparent original-code quirk
@@ -72,24 +72,21 @@ void Channel::playData(byte *soundData) {
/*-----------------------------------------------------------------------*/
RSound::RSound(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver, const Common::Path &filename,
- int dataOffset, int dataSize, int sysExOffset, RSoundFadeCheckMode fadeCheckMode) :
+ int dataOffset, int dataSize, int sysExOffset, RSoundFadeCheckMode fadeOutCheckMode) :
SoundDriver(mixer, filename, dataOffset, dataSize) {
_commandParam = 0;
_ticksSinceLastCommand = 0;
_ticksProcessingDisabled = false;
_mute = false;
- _masterVolume = 255;
- // FIXME RSOUND.008 initializes this to 0x005C; is the initial value different per driver?
- _randomSeed = 1234;
+ _randomSeed = 0x005C;
_runningStatus = 0;
_pollResult = 0;
_resultFlag = 0;
_sysExOffset = sysExOffset;
_midiDriver = midiDriver;
- _fadeCheckMode = fadeCheckMode;
- _fadeCheckAlternate = false;
- _fadeCheckCounter = 0;
- _fadeCheckPeriod = 0;
+ _fadeOutCheckMode = fadeOutCheckMode;
+ _fadeOutSpeed = (_fadeOutCheckMode == kRSoundFadeCheckAlternating ? 2 : 0);
+ _fadeOutCounter = _fadeOutSpeed;
for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i) {
_channels[i]._owner = this;
@@ -158,6 +155,9 @@ int RSound::stop() {
int RSound::poll() {
Common::StackLock slock(_driverMutex);
+ if (_paused)
+ return 0;
+
processTick();
int result = _pollResult;
@@ -214,48 +214,45 @@ bool RSound::isSoundPlaying(byte *pData) {
return false;
}
-int RSound::generateRandomNumber() {
- int v = 0x9249 + (int)_randomSeed;
- _randomSeed = ((v >> 3) | (v << 13)) & 0xFFFF;
+uint16 RSound::generateRandomNumber() {
+ uint16 newValue = 0x9249 + _randomSeed;
+ _randomSeed = ((newValue >> 3) | (newValue << 13)) & 0xFFFF;
return _randomSeed;
}
-void RSound::setVolume(int volume) {
- _masterVolume = CLIP(volume, 0, 255);
- for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i)
- sendVolume(i + 1, _mute ? 0 : _channels[i]._volume);
+int RSound::getTicksSinceLastCommand() {
+ return _ticksSinceLastCommand;
}
/*-----------------------------------------------------------------------*/
-void RSound::sendNoteOn(int midiChannel, int note, int velocity) {
+void RSound::sendNoteOn(byte midiChannel, byte note, byte velocity) {
_midiDriver->send(MidiDriver::MIDI_COMMAND_NOTE_ON | midiChannel, note, velocity);
}
-void RSound::sendProgramChange(int midiChannel, int program) {
+void RSound::sendProgramChange(byte midiChannel, byte program) {
_midiDriver->send(MidiDriver::MIDI_COMMAND_PROGRAM_CHANGE | midiChannel, program, 0);
}
-void RSound::sendVolume(int midiChannel, int volume) {
- const int scaledVolume = CLIP(volume, 0, 127) * _masterVolume / 255;
+void RSound::sendVolume(byte midiChannel, byte volume) {
_midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | midiChannel,
- MidiDriver::MIDI_CONTROLLER_VOLUME, scaledVolume);
+ MidiDriver::MIDI_CONTROLLER_VOLUME, volume);
}
-void RSound::sendPitchBend(int midiChannel, int value) {
+void RSound::sendPitchBend(byte midiChannel, byte value) {
// LSB always 0 - only coarse (MSB) control is used
_midiDriver->send(MidiDriver::MIDI_COMMAND_PITCH_BEND | midiChannel, 0, value);
}
-void RSound::sendPanning(int midiChannel, int value) {
+void RSound::sendPanning(byte midiChannel, byte value) {
_midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | midiChannel, MidiDriver::MIDI_CONTROLLER_PANNING, value);
}
-void RSound::muteChannel(int midiChannel) {
+void RSound::muteChannel(byte midiChannel) {
sendVolume(midiChannel, 0);
}
-void RSound::unmuteChannel(int midiChannel, int volume) {
+void RSound::unmuteChannel(byte midiChannel, byte volume) {
sendVolume(midiChannel, volume);
}
@@ -269,12 +266,7 @@ byte *RSound::sendSysExData(byte *pData, uint maxLength) {
return nullptr;
}
- // FIXME This call adds the necessary delay for the MT-32 to process the
- // SysEx message, which will make the engine unresponsive.
- // This can be fixed using the SysEx queue (specify true as 3rd param).
- // Driver status can then be checked from the main event loop using
- // _midiDriver->isReady().
- _midiDriver->sysExMT32(pData, length);
+ _midiDriver->sysExMT32(pData, length, true);
return &pData[length];
}
@@ -315,12 +307,16 @@ void RSound::sendSysExSequence() {
}
}
+void RSound::stopAllNotes() {
+ _midiDriver->stopAllNotes();
+}
+
/*-----------------------------------------------------------------------*/
void RSound::Channel_turnOffActiveNotes(Channel *channel) {
byte *channelActiveNotes = _activeNotes[channel->_midiChannel];
- for (int i = 0; i < 4; ++i) {
+ for (int i = 0; i < RSOUND_ACTIVE_NOTES_COUNT; ++i) {
if (channelActiveNotes[i] == 0xFF)
break;
@@ -330,9 +326,7 @@ void RSound::Channel_turnOffActiveNotes(Channel *channel) {
}
void RSound::Channel_processFadeOut(Channel *channel) {
- if (!channel->_deltaCounter)
- return;
- if (!channel->_fadeOutActive)
+ if (channel->_deltaCounter == 0 || !channel->_fadeOutActive)
return;
if (channel->_volume != 0) {
@@ -352,28 +346,32 @@ void RSound::Channel_processFadeOut(Channel *channel) {
}
void RSound::processChannelFadeOuts() {
- if (_fadeCheckMode == kRSoundFadeCheckAlternating) {
- _fadeCheckAlternate = !_fadeCheckAlternate;
- if (_fadeCheckAlternate)
- return;
- } else {
- if (!_fadeCheckPeriod)
- return;
- if (--_fadeCheckCounter > 0)
- return;
+ if (_fadeOutSpeed == 0 || --_fadeOutCounter > 0)
+ return;
- _fadeCheckCounter = _fadeCheckPeriod;
- }
+ _fadeOutCounter = _fadeOutSpeed;
for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i)
Channel_processFadeOut(&_channels[i]);
}
+void RSound::setFadeOutSpeed(int fadeOutSpeed) {
+ if (_fadeOutCheckMode == kRSoundFadeCheckProgrammable)
+ _fadeOutSpeed = fadeOutSpeed;
+}
+
void RSound::Channel_processTick(Channel *channel) {
- if (channel->_deltaCounter == 0)
+ if (channel->_deltaCounter == 0) {
+ channel->_pitchSlideStepSize = 0;
+ channel->_volumeFadeStepSize = 0;
+ channel->_panningSweepStepSize = 0;
+ // Original code continues with the effects processing code after the
+ // event processing loop, but because all effects have been disabled
+ // by setting step size to 0, this does nothing.
return;
+ }
- int midiChannel = channel->_midiChannel;
+ byte midiChannel = channel->_midiChannel;
if (channel->_noteDurationCounter > 0 && --channel->_noteDurationCounter == 0)
Channel_turnOffActiveNotes(channel);
@@ -383,207 +381,313 @@ void RSound::Channel_processTick(Channel *channel) {
while (!chordEventProcessed) {
byte *pSrc = channel->_pSrc;
- if (*pSrc < 0x80) {
- // Plain (note, duration) pair
+ if (pSrc[0] < 0x80) {
+ // Plain (note, delta) pair
byte note = pSrc[0];
- byte duration = pSrc[1];
+ byte delta = pSrc[1];
channel->_note = note;
- channel->_deltaCounter = duration;
- channel->_pSrc = pSrc + 2;
+ channel->_deltaCounter = delta;
+ channel->_pSrc += 2;
- if (!note || !duration) {
+ // Opcode 0x00: all notes off
+ // Delta 0: stop channel playback
+ if (note == 0 || delta == 0) {
Channel_turnOffActiveNotes(channel);
} else {
- channel->_noteDurationCounter = channel->_deltaCounter - channel->_noteDurationOffset;
-
- bool skipRetrigger = false;
- if ((int8)channel->_noteDurationOffset < 0 && _activeNotes[midiChannel][0] == note)
- skipRetrigger = true;
-
- if (!skipRetrigger) {
+ int duration = delta - channel->_noteDurationOffset;
+ if (duration < 0) {
+ // In the original code this calculation can result in underflow
+ // of the duration, which is a byte value. Not sure if this
+ // actually happens in any of the sound data.
+ warning("Calculated note duration is negative");
+ // Assignment to the noteDurationCounter byte field should
+ // result in the same behavior as the original code, i.e.
+ // duration -1 becomes counter value 0xFF.
+ }
+ channel->_noteDurationCounter = duration;
+
+ // If the note duration offset is 0 or positive, the notes played by
+ // the previous note or chord event had a duration that was equal to
+ // or shorter than the event delta and are already turned off, so the
+ // new note should always be played. Otherwise, if this note is
+ // already the first active note on the channel, the note should
+ // remain active and will not be retriggered.
+ // Note that a chord event could also have already played the new
+ // note as the second to fourth active note; this is not checked.
+ if (channel->_noteDurationOffset >= 0 || _activeNotes[midiChannel][0] != note) {
Channel_turnOffActiveNotes(channel);
sendNoteOn(midiChannel, note, channel->_velocity);
}
_activeNotes[midiChannel][0] = note;
}
+ // An event delta has been set, so break out of the event processing loop.
break;
}
// Opcode dispatch (bytes 0xF1-0xFF)
- int opcode = *pSrc - 0xF1;
+ byte opcode = pSrc[0];
switch (opcode) {
- case 0: // 0xF1: no-op (consume and ignore)
- channel->_pSrc = pSrc + 2;
+ case 0xF1: // No-op (consume and ignore)
+ channel->_pSrc += 2;
break;
- case 1: { // 0xF2: self-modifying randomize (byte swap in the data stream)
- int v1 = *++pSrc;
- ++pSrc;
- int v2 = (v1 - 1) & generateRandomNumber();
- int v3 = pSrc[v2];
- int v4 = pSrc[v1];
- pSrc[v4 + v1 + 1] = v3;
- channel->_pSrc += v1 + 3;
+ case 0xF2: { // Randomize
+ // Event data byte 1: number of values.
+ byte numValues = pSrc[1];
+ // Event data byte 2+: values.
+ // Generate a random index to pick one of the values.
+ byte randomIndex = (numValues - 1) & generateRandomNumber();
+ byte randomValue = pSrc[2 + randomIndex];
+ // Last event data byte: data offset where the random value should be written.
+ byte dataOffset = pSrc[2 + numValues];
+ // Advance sound data pointer to after this event.
+ channel->_pSrc += numValues + 3;
+ // Write the random value at the data offset after this event.
+ channel->_pSrc[dataOffset] = randomValue;
break;
}
- case 2: // 0xF3: setup pan fade
+ case 0xF3: // Start panning sweep
channel->_panningSweepSpeed = pSrc[1];
- channel->_panningSweepStepSize = pSrc[2];
+ channel->_panningSweepStepSize = (int8) pSrc[2];
channel->_panningSweepCounter = 1;
- channel->_pSrc = pSrc + 3;
+ channel->_pSrc += 3;
break;
- case 3: // 0xF4: set pan directly and send
+ case 0xF4: // Panning
channel->_panning = pSrc[1];
sendPanning(midiChannel, channel->_panning);
- channel->_pSrc = pSrc + 2;
+ channel->_pSrc += 2;
break;
- case 4: { // 0xF5: chord note-on trigger (up to 4 simultaneous notes)
+ case 0xF5: { // Chord
+ // Event data byte 1: number of notes in the chord
byte noteCount = pSrc[1];
+ byte noteBytesCount = pSrc[1];
+ if (noteCount > RSOUND_ACTIVE_NOTES_COUNT) {
+ // The original code does not check the note count.
+ // It will play more than 4 notes and write them
+ // into the active note data of the next channel
+ // or out of bounds. Some tracks contain chords
+ // with more than 4 notes.
+ warning("Encountered chord event with note count %i; limiting to %i notes", noteCount, RSOUND_ACTIVE_NOTES_COUNT);
+ noteCount = RSOUND_ACTIVE_NOTES_COUNT;
+ }
+ // Event data byte 2+: note values
byte *notes = pSrc + 2;
- byte *slots = _activeNotes[midiChannel];
+ byte *activeNotes = _activeNotes[midiChannel];
int i;
for (i = 0; i < noteCount; ++i) {
byte note = notes[i];
- if (slots[i] != note) {
+ // Do not play the note again if it is already registered
+ // at this index in the active notes data. The note could
+ // also be registered at another index, but this is not
+ // checked.
+ if (activeNotes[i] != note) {
sendNoteOn(midiChannel, note, channel->_velocity);
- slots[i] = note;
+ activeNotes[i] = note;
}
}
- for (; i < 4; ++i)
- slots[i] = 0xFF;
-
- byte duration = notes[noteCount];
- channel->_deltaCounter = duration;
- if (channel->_noteDurationOffset != 0xFF) {
- channel->_noteDurationCounter = (duration < channel->_noteDurationOffset) ?
- duration : duration - channel->_noteDurationOffset;
+ // Clear the remaining active note entries for this channel.
+ for (; i < RSOUND_ACTIVE_NOTES_COUNT; ++i)
+ activeNotes[i] = 0xFF;
+
+ // Last event data byte: delta
+ byte delta = pSrc[noteBytesCount + 2];
+ channel->_deltaCounter = delta;
+ // Subtract note duration offset from the delta to get the
+ // chord duration.
+ // Note that the duration is determined here in a different
+ // way than for a note event. The offset is checked for
+ // the value -1 instead of < 0, suggesting that -1 is the
+ // minimum value. Also, a check is added to make sure the
+ // duration does not underflow.
+ byte duration;
+ if (channel->_noteDurationOffset == -1) {
+ duration = delta + 1;
} else {
- channel->_noteDurationCounter = duration + 1;
+ duration = (delta < channel->_noteDurationOffset) ? delta : delta - channel->_noteDurationOffset;
}
+ channel->_noteDurationCounter = duration;
- channel->_pSrc = pSrc + noteCount + 3;
+ channel->_pSrc += noteBytesCount + 3;
+ // An event delta has been set, so break out of the event
+ // processing loop.
chordEventProcessed = true;
break;
}
- case 5: // 0xF6: set volume directly and send (priority-gated while pending-stop)
+ case 0xF6: // Volume
+ // If the channel is fading out to stop and the new volume
+ // value is higher than the current value, do not set
+ // the new value.
if (!channel->_fadeOutActive || channel->_volume >= pSrc[1]) {
channel->_volume = pSrc[1];
sendVolume(midiChannel, channel->_volume);
}
- channel->_pSrc = pSrc + 2;
+ channel->_pSrc += 2;
break;
- case 6: // 0xF7: set pitch bend directly and send
+ case 0xF7: // Pitch bend
+ // Only the pitch bend MSB is used.
channel->_pitchBend = pSrc[1];
sendPitchBend(midiChannel, channel->_pitchBend);
- channel->_pSrc = pSrc + 2;
+ channel->_pSrc += 2;
break;
- case 7: // 0xF8: setup volume fade (gated by pending-stop)
+ case 0xF8: // Start volume fade
+ // If the channel is already fading out to stop, do not start
+ // a volume fade.
if (!channel->_fadeOutActive) {
channel->_volumeFadeSpeed = pSrc[1];
channel->_volumeFadeCounter = pSrc[1];
- channel->_volumeFadeStepSize = pSrc[2];
+ channel->_volumeFadeStepSize = (int8) pSrc[2];
}
- channel->_pSrc = pSrc + 3;
+ channel->_pSrc += 3;
break;
- case 8: // 0xF9: set velocity
+ case 0xF9: // Note velocity
+ // Velocity is not part of a note event; it is set using this
+ // event as a channel setting.
channel->_velocity = pSrc[1];
- channel->_pSrc = pSrc + 2;
+ channel->_pSrc += 2;
break;
- case 9: // 0xFA: setup pitch-bend ramp
+ case 0xFA: // Start pitch slide
channel->_pitchSlideSpeed = pSrc[1];
channel->_pitchSlideStepSize = (int8) pSrc[2];
channel->_pitchSlideDurationCounter = pSrc[3];
channel->_pitchSlideCounter = 1;
- channel->_pSrc = pSrc + 4;
+ channel->_pSrc += 4;
break;
- case 10: // 0xFB: set note offset
- channel->_noteDurationOffset = pSrc[1];
- channel->_pSrc = pSrc + 2;
+ case 0xFB: // Note duration offset
+ channel->_noteDurationOffset = (int8) pSrc[1];
+ channel->_pSrc += 2;
break;
- case 11: // 0xFC: program change
+ case 0xFC: // Program change
channel->_program = pSrc[1];
sendProgramChange(midiChannel, channel->_program);
- channel->_pSrc = pSrc + 2;
+ channel->_pSrc += 2;
break;
- case 12: { // 0xFD: full reset / loop restart, preserving pending-stop
+ case 0xFD: { // Loop to start
+ // Reload data to completely reset channel state, but keep
+ // fade-out to stop state.
bool fadeOutActive = channel->_fadeOutActive;
channel->loadData(channel->_soundDataStart);
channel->_fadeOutActive = fadeOutActive;
break;
}
- case 13: // 0xFE: outer loop
- if (!channel->_outerLoopCounter) {
- if (*++pSrc == 0) {
+ case 0xFE: { // Outer loop
+ byte repeats = pSrc[1];
+ if (channel->_outerLoopCounter == 0) {
+ // Outer loop is inactive. Set loop start point or start looping.
+ if (repeats == 0) {
+ // Event data byte is 0: set loop start point after this event.
+ // Also set inner loop start point to keep it contained within
+ // the outer loop.
channel->_pSrc += 2;
channel->_outerLoopStart = channel->_pSrc;
channel->_innerLoopStart = channel->_pSrc;
- channel->_innerLoopCounter = 0;
channel->_outerLoopCounter = 0;
+ channel->_innerLoopCounter = 0;
} else {
- channel->_outerLoopCounter =
- (uint16)(int16)(int8)*pSrc;
+ // Event data byte is > 0: set number of repeats and loop back
+ // to the loop start point. Also reset the inner loop start
+ // point to the outer loop start point.
+ channel->_outerLoopCounter = repeats;
channel->_pSrc = channel->_outerLoopStart;
channel->_innerLoopStart = channel->_outerLoopStart;
}
- } else if (--channel->_outerLoopCounter) {
+ } else if (--channel->_outerLoopCounter > 0) {
+ // Outer loop is active and counter is >= 1: loop back to the loop
+ // start point. Also reset the inner loop start point to the outer
+ // loop start point.
channel->_pSrc = channel->_outerLoopStart;
channel->_innerLoopStart = channel->_outerLoopStart;
} else {
+ // Outer loop is active and was counted down to 0. Continue playback
+ // with the first event after the loop end point.
+ // This also sets both loop start points to that event.
channel->_pSrc += 2;
channel->_outerLoopStart = channel->_pSrc;
channel->_innerLoopStart = channel->_pSrc;
}
break;
+ }
- case 14: // 0xFF: inner loop
- if (!channel->_innerLoopCounter) {
- if (*++pSrc == 0) {
+ case 0xFF: { // Inner loop
+ byte repeats = pSrc[1];
+ if (channel->_innerLoopCounter == 0) {
+ // Inner loop is inactive. Set loop start point or start looping.
+ if (repeats == 0) {
+ // Event data byte is 0: set loop start point after this event.
channel->_pSrc += 2;
channel->_innerLoopStart = channel->_pSrc;
channel->_innerLoopCounter = 0;
} else {
- channel->_innerLoopCounter =
- (uint16)(int16)(int8)*pSrc;
+ // Event data byte is > 0: set number of repeats and loop back
+ // to the loop start point.
+ channel->_innerLoopCounter = repeats;
channel->_pSrc = channel->_innerLoopStart;
}
- } else if (--channel->_innerLoopCounter) {
+ } else if (--channel->_innerLoopCounter > 0) {
+ // Inner loop is active and counter is >= 1: loop back to the loop
+ // start point.
channel->_pSrc = channel->_innerLoopStart;
} else {
+ // Inner loop is active and was counted down to 0. Continue playback
+ // with the first event after the loop end point.
+ // This also sets the loop start point to that event.
channel->_pSrc += 2;
channel->_innerLoopStart = channel->_pSrc;
}
break;
-
+ }
+
default:
- break;
+ // When encountering an invalid opcode, the original code would
+ // not advance the sound data pointer or break out of the event
+ // processing loop. This causes the code to hang on this opcode.
+ // Instead, playback for this channel is stopped here.
+ warning("Invalid sound opcode %02X encountered; stopping channel", opcode);
+ channel->_deltaCounter = 0;
+ return;
}
}
}
- // Fade tail: pitch bend, volume, pan
+ // Process effects
+
+ // Volume fade
+ if (channel->_volumeFadeStepSize > 0 && --channel->_volumeFadeCounter == 0) {
+ channel->_volumeFadeCounter = channel->_volumeFadeSpeed;
+ int newVolume = channel->_volume + channel->_volumeFadeStepSize;
+ if (newVolume < 0 || newVolume > 0x7F) {
+ // Volume has reached minimum or maximum volume. Stop the fade.
+ channel->_volumeFadeStepSize = 0;
+ newVolume = CLIP(newVolume, 0, 0x7F);
+ }
+ channel->_volume = newVolume;
+ sendVolume(midiChannel, newVolume);
+ }
+
+ // Pitch slide
if (channel->_pitchSlideStepSize != 0) {
if (--channel->_pitchSlideCounter == 0) {
channel->_pitchSlideCounter = channel->_pitchSlideSpeed;
int newPitchBend = channel->_pitchBend + channel->_pitchSlideStepSize;
if (newPitchBend < 0 || newPitchBend > 0x7F) {
- // Overflow will cause the driver to output invalid MIDI events.
+ // There is no bounds checking in the original code. Overflow or
+ // underflow will cause the driver to output invalid MIDI events.
// This actually happens in the original code, in the intro,
// when Rex lands his ship.
- warning("Pitch bend overflow; terminating pitch slide");
+ warning("Pitch bend overflow or underflow; terminating pitch slide");
channel->_pitchSlideStepSize = 0;
newPitchBend = CLIP(newPitchBend, 0, 0x7F);
}
@@ -594,34 +698,22 @@ void RSound::Channel_processTick(Channel *channel) {
channel->_pitchSlideStepSize = 0;
}
- if (channel->_volumeFadeStepSize) {
- if (!--channel->_volumeFadeCounter) {
- channel->_volumeFadeCounter = channel->_volumeFadeSpeed;
- int volumeSum = (int8)channel->_volume + (int8)channel->_volumeFadeStepSize;
- if (volumeSum < 0) {
- channel->_volumeFadeStepSize = 0;
- volumeSum = 0;
- } else if (volumeSum >= 0x7F) {
- channel->_volumeFadeStepSize = 0;
- volumeSum = 0x7F;
- }
- int8 newVolume = (int8)volumeSum;
- channel->_volume = newVolume;
- sendVolume(midiChannel, newVolume);
- }
- }
-
- if (channel->_panningSweepStepSize) {
- if (!--channel->_panningSweepCounter) {
- channel->_panningSweepCounter = channel->_panningSweepSpeed;
- byte newPan = channel->_panning + channel->_panningSweepStepSize;
- if (newPan > 0x7F) {
- newPan = (newPan ^ 0x7F) & 0x7F;
- channel->_panningSweepCounter = 0;
- }
- channel->_panning = newPan;
- sendPanning(midiChannel, newPan);
+ // Panning sweep
+ if (channel->_panningSweepStepSize != 0 && --channel->_panningSweepCounter == 0) {
+ channel->_panningSweepCounter = channel->_panningSweepSpeed;
+ byte newPanning = channel->_panning + channel->_panningSweepStepSize;
+ if (newPanning > 0x7F) {
+ // This inverts overflow or underflow back into the valid range
+ // of 0x00-0x7F; f.e. -3 becomes 2 and 0x83 becomes 0x7C.
+ newPanning = (newPanning ^ 0x7F) & 0x7F;
+ // This causes the next sweep step to be executed in 256 ticks.
+ // This is accurate to the original code, but maybe the
+ // intention was to stop the sweep by setting the step size
+ // to 0?
+ channel->_panningSweepCounter = 0;
}
+ channel->_panning = newPanning;
+ sendPanning(midiChannel, newPanning);
}
}
@@ -672,7 +764,7 @@ void RSound::resetChannelRange(int firstChannel, int lastChannel, bool includeCh
void RSound::clearActiveNotes() {
for (int i = 0; i < RSOUND_CHANNEL_COUNT + 1; ++i)
- for (int j = 0; j < 4; ++j)
+ for (int j = 0; j < RSOUND_ACTIVE_NOTES_COUNT; ++j)
_activeNotes[i][j] = 0xFF;
}
@@ -680,10 +772,14 @@ void RSound::clearActiveNotesRange(int firstChannel, int lastChannel) {
assert(firstChannel >= 1 && lastChannel <= RSOUND_CHANNEL_COUNT &&
firstChannel <= lastChannel);
for (int channel = firstChannel; channel <= lastChannel; ++channel)
- for (int slot = 0; slot < 4; ++slot)
+ for (int slot = 0; slot < RSOUND_ACTIVE_NOTES_COUNT; ++slot)
_activeNotes[channel][slot] = 0xFF;
}
+byte *RSound::loadData(int offset) {
+ return &_soundData[offset];
+}
+
/**
* Resets all 9 channels and the active notes table.
* Called both from the constructor (mirroring rsound_init) and from
@@ -710,7 +806,7 @@ void RSound::sendMidiChannelReset(int first, int last) {
int RSound::command0() {
resetAllChannels();
- setFadeCheckPeriod(0);
+ setFadeOutSpeed(0);
sendMidiChannelReset(1, RSOUND_CHANNEL_COUNT);
// Matches the tail of the original rsound_command0.
@@ -724,7 +820,7 @@ int RSound::command0() {
}
int RSound::command1() {
- setFadeCheckPeriod(1);
+ setFadeOutSpeed(1);
for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i)
_channels[i].setFadeOut(true);
return 0;
@@ -735,7 +831,7 @@ int RSound::command2() {
// table) plus the MIDI channel reset for those same channels.
resetChannelRange(1, 4, true);
clearActiveNotes();
- setFadeCheckPeriod(0);
+ setFadeOutSpeed(0);
// The original code does not reset MIDI channel 9, which is
// probably an oversight.
sendMidiChannelReset(1, 4);
@@ -745,7 +841,7 @@ int RSound::command2() {
int RSound::command3() {
// Start fade-out to stop for channels 1-4 and 9.
- setFadeCheckPeriod(1);
+ setFadeOutSpeed(1);
for (int i = 0; i < 4; ++i)
_channels[i].setFadeOut(true);
_channels[8].setFadeOut(true);
@@ -756,7 +852,7 @@ int RSound::command4() {
// Channels 5-8 (does NOT touch the held-notes
// table) plus the MIDI channel reset for those same channels.
resetChannelRange(5, 8);
- setFadeCheckPeriod(0);
+ setFadeOutSpeed(0);
// The original code also resets MIDI channel 9, which is
// probably incorrect.
sendMidiChannelReset(5, 8);
@@ -765,7 +861,7 @@ int RSound::command4() {
int RSound::command5() {
// Start fade-out to stop for channels 5-8.
- setFadeCheckPeriod(1);
+ setFadeOutSpeed(1);
for (int i = 4; i < 8; ++i)
_channels[i].setFadeOut(true);
return 0;
@@ -795,6 +891,10 @@ int RSound::command8() {
return result;
}
+int RSound::nullCommand() {
+ return 0;
+}
+
} // namespace Sound
} // namespace RexNebular
} // namespace MADS
diff --git a/engines/mads/nebular/sound/rsound.h b/engines/mads/nebular/sound/rsound.h
index b6257b02443..cee08d24970 100644
--- a/engines/mads/nebular/sound/rsound.h
+++ b/engines/mads/nebular/sound/rsound.h
@@ -34,7 +34,14 @@ namespace Sound {
class RSound;
#define RSOUND_CHANNEL_COUNT 9
-
+// The number of active notes registered per channel.
+// The original code used 4, but some chord events contain more notes and
+// overwrite in the active notes for the next channel.
+#define RSOUND_ACTIVE_NOTES_COUNT 8
+
+// Mode for fade-out to stop checks.
+// kRSoundFadeCheckAlternating: fixed speed of 2 (every other tick)
+// kRSoundFadeCheckProgrammable: speed is programmable using setFadeOutSpeed
enum RSoundFadeCheckMode {
kRSoundFadeCheckAlternating,
kRSoundFadeCheckProgrammable
@@ -72,37 +79,37 @@ enum RSoundFadeCheckMode {
class Channel {
public:
RSound *_owner = nullptr;
- int _midiChannel = 0; // 1-9: the MIDI channel to which the data in this struct pertains
-
- int _deltaCounter = 0; // number of ticks until the next event occurs; loaded from the delta byte of a note or chord event
- // 0: channel is not active
- int _pitchSlideStepSize = 0; // delta added to _pitchBend each pitch-slide step; 0: pitch slide is not active
- int _volumeFadeStepSize = 0; // delta added to _volume each volume fade step; 0: volume fade is not active
- int _panningSweepStepSize = 0; // delta added to _panning each panning sweep step; 0: panning sweep is not active
- int _note = 0; // MIDI note number, read from the note or chord event
- int _program = 0; // patch/instrument number, sent as a Program Change
- int _velocity = 0; // note velocity, used by RSound::sendNoteOn()
- int _noteDurationOffset = 0; // subtracted from the event delta to derive the note duration; positive offset: note is turned off
- // before the next event is processed. Data might only use up to -1 in the negative direction.
- int _noteDurationCounter = 0; // number of ticks until the currently active note(s) is/are turned off
- int _volumeFadeCounter = 0; // number of ticks until the next volume fade step is processed
- int _pitchSlideCounter = 0; // number of ticks until the next pitch slide step is processed
- int _panningSweepCounter = 0; // number of ticks until the next panning sweep step is processed
- int _volume = 0; // current channel volume (MIDI CC#7)
- int _pitchBend = 0; // current pitch bend value (status 0xEn, coarse/MSB only); 0x40 = center
- int _panning = 0; // current pan value (MIDI CC#10); 0x40 = center
- int _volumeFadeSpeed = 0; // number of ticks between volume fade steps
- int _pitchSlideSpeed = 0; // number of ticks between pitch slide steps
- int _panningSweepSpeed = 0; // number of ticks between panning sweep steps
- int _pitchSlideDurationCounter = 0; // number of ticks until the pitch slide ends
- bool _fadeOutActive = false; // true while the channel is fading out to silence (not to be confused with a volume fade)
- byte *_soundDataStart = nullptr; // start of the sound data stream playing on this channel
- byte *_pSrc = nullptr; // current read pointer into the sound data stream
- byte *_innerLoopStart = nullptr; // inner loop restart address
- byte *_outerLoopStart = nullptr; // outer loop restart address
- int _innerLoopCounter = 0; // number of repeats of the inner loop remaining
- int _outerLoopCounter = 0; // number of repeats of the outer loop remaining
- byte *_soundData = nullptr; // identifies the sound data played by this channel; effectively the same as _soundDataStart
+ byte _midiChannel = 0; // 1-9: the MIDI channel to which the data in this struct pertains
+
+ byte _deltaCounter = 0; // number of ticks until the next event occurs; loaded from the delta byte of a note or chord event
+ // 0: channel is not active
+ int8 _pitchSlideStepSize = 0; // delta added to _pitchBend each pitch-slide step; 0: pitch slide is not active
+ int8 _volumeFadeStepSize = 0; // delta added to _volume each volume fade step; 0: volume fade is not active
+ int8 _panningSweepStepSize = 0; // delta added to _panning each panning sweep step; 0: panning sweep is not active
+ byte _note = 0; // MIDI note number, read from the note or chord event
+ byte _program = 0; // patch/instrument number, sent as a Program Change
+ byte _velocity = 0; // note velocity, used by RSound::sendNoteOn()
+ int8 _noteDurationOffset = 0; // subtracted from the event delta to derive the note duration; positive offset: note is turned off
+ // before the next event is processed. Data might only use up to -1 in the negative direction.
+ byte _noteDurationCounter = 0; // number of ticks until the currently active note(s) is/are turned off
+ byte _volumeFadeCounter = 0; // number of ticks until the next volume fade step is processed
+ byte _pitchSlideCounter = 0; // number of ticks until the next pitch slide step is processed
+ byte _panningSweepCounter = 0; // number of ticks until the next panning sweep step is processed
+ byte _volume = 0; // current channel volume (MIDI CC#7)
+ byte _pitchBend = 0; // current pitch bend value (status 0xEn, coarse/MSB only); 0x40 = center
+ byte _panning = 0; // current pan value (MIDI CC#10); 0x40 = center
+ byte _volumeFadeSpeed = 0; // number of ticks between volume fade steps
+ byte _pitchSlideSpeed = 0; // number of ticks between pitch slide steps
+ byte _panningSweepSpeed = 0; // number of ticks between panning sweep steps
+ byte _pitchSlideDurationCounter = 0; // number of ticks until the pitch slide ends
+ bool _fadeOutActive = false; // true while the channel is fading out to silence (not to be confused with a volume fade)
+ byte *_soundDataStart = nullptr; // start of the sound data stream playing on this channel
+ byte *_pSrc = nullptr; // current read pointer into the sound data stream
+ byte *_innerLoopStart = nullptr; // inner loop restart address
+ byte *_outerLoopStart = nullptr; // outer loop restart address
+ byte _innerLoopCounter = 0; // number of repeats of the inner loop remaining
+ byte _outerLoopCounter = 0; // number of repeats of the outer loop remaining
+ byte *_soundData = nullptr; // identifies the sound data played by this channel; effectively the same as _soundDataStart
public:
Channel() {}
@@ -139,14 +146,15 @@ class RSound : public SoundDriver {
friend class Channel;
private:
uint16 _randomSeed;
- int _masterVolume;
- byte _runningStatus; // running-status cache, avoids resending an unchanged status byte
- // Note that the ScummVM MIDI drivers do not use this; they always send the status byte
- byte _activeNotes[RSOUND_CHANNEL_COUNT + 1][4]; // The note(s) currently playing on each MIDI channel (index 0 unused; channels are 1-9)
- RSoundFadeCheckMode _fadeCheckMode;
- bool _fadeCheckAlternate;
- int _fadeCheckCounter;
- int _fadeCheckPeriod;
+ // running-status cache, avoids resending an unchanged status byte
+ // Note that the ScummVM MIDI drivers do not use this; they always send the status byte
+ byte _runningStatus;
+ // The note(s) currently playing on each MIDI channel (index 0 unused; channels are 1-9)
+ byte _activeNotes[RSOUND_CHANNEL_COUNT + 1][RSOUND_ACTIVE_NOTES_COUNT];
+
+ RSoundFadeCheckMode _fadeOutCheckMode;
+ int _fadeOutCounter;
+ int _fadeOutSpeed;
/**
* Data-segment offset of this driver's own "command0_array" (the
@@ -163,6 +171,26 @@ private:
void processTick();
void processTickAllChannels();
+ /**
+ * Brief description of the event loop:
+ * This function reads an opcode byte plus a number of data bytes (depending on the operation)
+ * from the sound data stream. Opcodes 0x00 - 0x7F are note events, playing the MIDI note
+ * indicated by the opcode. Opcodes 0xF1-0xFF do things like setting volume, program change
+ * and starting a panning sweep. Opcode 0xF5 plays a chord i.e. multiple notes at the same
+ * time (up to 4). The note events and the chord event are the only events that specify a
+ * delta as (one of) the data byte(s). This delta is the number of ticks until the next event
+ * should be processed. The other events are processed one after the other in the same tick,
+ * until an event with a delta is encountered.
+ * The note and chord events also have a duration, which is the number of ticks until the
+ * played note(s) is/are turned off. By default this duration is equal to the event delta,
+ * but this can be changed by setting the note duration offset (opcode 0xFB). This offset is
+ * subtracted from the event delta to determine the note duration. So a positive offset will
+ * cause notes to end before the next event is processed, while a negative offset will overlap
+ * the notes with the next events. The MIDI convention of pairing note on events with note off
+ * events is not used, but opcode 0x00 can be used to turn off all active notes.
+ * Specifying an event delta of 0 will stop playback. Because of this, when starting playback
+ * of sound data for a channel, a positive delta (usually 1) must be set on the channel.
+ */
void Channel_processTick(Channel *channel);
/**
@@ -189,10 +217,7 @@ private:
protected:
int _commandParam;
- void setFadeCheckPeriod(int period) {
- if (_fadeCheckMode == kRSoundFadeCheckProgrammable)
- _fadeCheckPeriod = period;
- }
+ void setFadeOutSpeed(int fadeOutSpeed);
/**
* Clear the active and fade state for MIDI channels in [first, last].
@@ -206,9 +231,7 @@ protected:
/** Reset active note slots for the inclusive MIDI-channel range. */
void clearActiveNotesRange(int firstChannel, int lastChannel);
- byte *loadData(int offset) {
- return &_soundData[offset];
- }
+ byte *loadData(int offset);
/**
* Hook called once per processTick() frame, immediately after the disabled
@@ -250,17 +273,17 @@ protected:
*/
bool isSoundPlaying(byte *pData);
- int generateRandomNumber();
+ uint16 generateRandomNumber();
// ---- Low-level MIDI send helpers -------------------------------
// All send through the ScummVM MT-32 / General MIDI driver.
- void sendNoteOn(int midiChannel, int note, int velocity);
- void sendProgramChange(int midiChannel, int program);
- void sendVolume(int midiChannel, int volume);
- void sendPitchBend(int midiChannel, int value);
- void sendPanning(int midiChannel, int value);
- void muteChannel(int midiChannel);
- void unmuteChannel(int midiChannel, int volume);
+ void sendNoteOn(byte midiChannel, byte note, byte velocity);
+ void sendProgramChange(byte midiChannel, byte program);
+ void sendVolume(byte midiChannel, byte volume);
+ void sendPitchBend(byte midiChannel, byte value);
+ void sendPanning(byte midiChannel, byte value);
+ void muteChannel(byte midiChannel);
+ void unmuteChannel(byte midiChannel, byte volume);
/**
* Resets the MIDI channel state (all notes off, reset all
@@ -292,6 +315,12 @@ protected:
*/
void sendSysExSequence();
+ /**
+ * Stop all notes playing on the device. Used when pausing or
+ * quitting the engine.
+ */
+ void stopAllNotes() override;
+
virtual int command0();
int command1();
int command2();
@@ -302,9 +331,7 @@ protected:
int command7();
int command8();
- int nullCommand() {
- return 0;
- }
+ int nullCommand();
public:
Channel _channels[RSOUND_CHANNEL_COUNT];
@@ -338,11 +365,7 @@ public:
void noise() override {
// No equivalent in the Roland driver - noise() is an Adlib/OPL-only concept
}
- void setVolume(int volume) override;
-
- int getTicksSinceLastCommand() {
- return _ticksSinceLastCommand;
- }
+ int getTicksSinceLastCommand();
};
} // namespace Sound
diff --git a/engines/mads/nebular/sound/rsound_nebular.cpp b/engines/mads/nebular/sound/rsound_nebular.cpp
index ce7152af906..024157a727b 100644
--- a/engines/mads/nebular/sound/rsound_nebular.cpp
+++ b/engines/mads/nebular/sound/rsound_nebular.cpp
@@ -939,7 +939,7 @@ Channel *RSound3::method1(int offset, byte value) {
}
void RSound3::resetUpperChannelsTail() {
- setFadeCheckPeriod(1);
+ setFadeOutSpeed(1);
_channels[4].setFadeOut(true);
_channels[5].setFadeOut(true);
_channels[6].setFadeOut(true);
@@ -965,7 +965,7 @@ int RSound3::command5() {
int RSound3::command9() {
command1();
- setFadeCheckPeriod((byte)_commandParam);
+ setFadeOutSpeed((byte)_commandParam);
return 0;
}
@@ -1043,7 +1043,7 @@ int RSound3::command15() {
_channels[5]._fadeOutActive = true;
_channels[6]._fadeOutActive = true;
_channels[7]._fadeOutActive = true;
- setFadeCheckPeriod(1);
+ setFadeOutSpeed(1);
return 0;
}
@@ -1349,7 +1349,7 @@ void RSound4::tickCallback() {
int RSound4::command9() {
command1();
- setFadeCheckPeriod((byte)_commandParam);
+ setFadeOutSpeed((byte)_commandParam);
return 0;
}
diff --git a/engines/mads/nebular/sound/sound.cpp b/engines/mads/nebular/sound/sound.cpp
index ad34b4f2be4..2b72556ff2f 100644
--- a/engines/mads/nebular/sound/sound.cpp
+++ b/engines/mads/nebular/sound/sound.cpp
@@ -51,6 +51,8 @@ RexSoundManager::RexSoundManager(Audio::Mixer *mixer, bool &soundFlag,
if (returnCode != 0)
error("SoundManager - Failed to open MIDI music driver - error code %d.", returnCode);
+ _midiDriver->property(MidiDriver::PROP_USER_VOLUME_SCALING, true);
+
_driverCallbackDelta = _midiDriver->getBaseTempo();
_midiDriver->setTimerCallback(this, &timerCallback);
}
Commit: 140dfaa9ed7134eaecbcc2f63ac9f9e2c7f66e84
https://github.com/scummvm/scummvm/commit/140dfaa9ed7134eaecbcc2f63ac9f9e2c7f66e84
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-10-09T18:19:49+02:00
Commit Message:
MADS: More Rex MT-32 refactoring
Changed paths:
engines/mads/nebular/sound/rsound.cpp
engines/mads/nebular/sound/rsound.h
engines/mads/nebular/sound/rsound_nebular.cpp
engines/mads/nebular/sound/rsound_nebular.h
diff --git a/engines/mads/nebular/sound/rsound.cpp b/engines/mads/nebular/sound/rsound.cpp
index 8672b4a0cc2..3d01b5c5651 100644
--- a/engines/mads/nebular/sound/rsound.cpp
+++ b/engines/mads/nebular/sound/rsound.cpp
@@ -58,17 +58,6 @@ void Channel::setFadeOut(bool fadeOut) {
}
}
-void Channel::playData(byte *soundData) {
- bool ticksProcessingDisabled = _owner->_ticksProcessingDisabled;
- _owner->_ticksProcessingDisabled = true;
-
- loadData(soundData);
- _deltaCounter = 1;
- _owner->sendPitchBend(_midiChannel, 0x40);
-
- _owner->_ticksProcessingDisabled = ticksProcessingDisabled;
-}
-
/*-----------------------------------------------------------------------*/
RSound::RSound(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver, const Common::Path &filename,
@@ -89,7 +78,6 @@ RSound::RSound(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver, const Common:
_fadeOutCounter = _fadeOutSpeed;
for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i) {
- _channels[i]._owner = this;
_channels[i]._midiChannel = i + 1;
}
@@ -172,48 +160,75 @@ void RSound::resultCheck() {
}
}
-Channel *RSound::playSoundCh5To8(int offset) {
+Channel *RSound::getChannel(byte channel) {
+ return &_channels[channel - 1];
+}
+
+Channel *RSound::playSoundStatic(byte *soundData, byte channel) {
+ assert(channel >= 1 && channel <= RSOUND_CHANNEL_COUNT);
+
+ Channel *chan = getChannel(channel);
+ Channel_playData(chan, soundData);
+
+ return chan;
+}
+
+Channel *RSound::playSoundStatic(int offset, byte channel) {
+ return playSoundStatic(loadData(offset), channel);
+}
+
+Channel *RSound::playSoundDynamic(int offset) {
return allocateAndPlay(loadData(offset));
}
-Channel *RSound::playSoundCh1To8(int offset) {
- return allocateAndPlay(loadData(offset), 0);
+Channel *RSound::playSoundAnyChannel(int offset) {
+ return allocateAndPlay(loadData(offset), 1);
}
Channel *RSound::allocateAndPlay(byte *pData, int startingChannel) {
int endChannel = RSOUND_CHANNEL_COUNT - 1;
+ Channel *foundChannel = nullptr;
+
// Scan for a free channel
- for (int i = startingChannel; i < endChannel; ++i) {
- if (_channels[i]._deltaCounter == 0) {
- _channels[i].playData(pData);
- return &_channels[i];
+ for (int i = startingChannel; i <= endChannel; ++i) {
+ if (getChannel(i)->_deltaCounter == 0) {
+ foundChannel = getChannel(i);
+ break;
}
}
- // None found; fall back to a channel that is fading out to stop
- for (int i = endChannel - 1; i >= startingChannel; --i) {
- if (_channels[i]._fadeOutActive) {
- _channels[i].playData(pData);
- return &_channels[i];
+ if (foundChannel == nullptr) {
+ // None found; fall back to a channel that is fading out to stop
+ for (int i = endChannel; i >= startingChannel; --i) {
+ if (getChannel(i)->_fadeOutActive) {
+ foundChannel = getChannel(i);
+ break;
+ }
}
}
- // No available channel
- return nullptr;
+ if (foundChannel != nullptr)
+ Channel_playData(foundChannel, pData);
+
+ return foundChannel;
}
bool RSound::isSoundPlaying(byte *pData) {
- // Deliberately excludes channel 9 (index 8), matching the disassembly -
+ // Deliberately excludes channel 9, matching the disassembly -
// same as allocateAndPlay()'s scan never reaching channel 9 either.
- for (int i = 0; i < RSOUND_CHANNEL_COUNT - 1; ++i) {
- if (_channels[i]._deltaCounter > 0 && _channels[i]._soundData == pData)
+ for (int i = 1; i < RSOUND_CHANNEL_COUNT; ++i) {
+ if (isSoundPlaying(i, pData))
return true;
}
return false;
}
+bool RSound::isSoundPlaying(byte channel, byte *pData) {
+ return getChannel(channel)->_deltaCounter > 0 && getChannel(channel)->_soundData == pData;
+}
+
uint16 RSound::generateRandomNumber() {
uint16 newValue = 0x9249 + _randomSeed;
_randomSeed = ((newValue >> 3) | (newValue << 13)) & 0xFFFF;
@@ -256,6 +271,11 @@ void RSound::unmuteChannel(byte midiChannel, byte volume) {
sendVolume(midiChannel, volume);
}
+void RSound::setChannelVolume(byte channel, byte volume) {
+ getChannel(channel)->_volume = volume;
+ sendVolume(channel, volume);
+}
+
byte *RSound::sendSysExData(byte *pData, uint maxLength) {
uint length = 0;
while (length < maxLength && pData[length] != 0xFF)
@@ -313,6 +333,17 @@ void RSound::stopAllNotes() {
/*-----------------------------------------------------------------------*/
+void RSound::Channel_playData(Channel *channel, byte *soundData) {
+ bool ticksProcessingDisabled = _ticksProcessingDisabled;
+ _ticksProcessingDisabled = true;
+
+ channel->loadData(soundData);
+ channel->_deltaCounter = 1;
+ sendPitchBend(channel->_midiChannel, 0x40);
+
+ _ticksProcessingDisabled = ticksProcessingDisabled;
+}
+
void RSound::Channel_turnOffActiveNotes(Channel *channel) {
byte *channelActiveNotes = _activeNotes[channel->_midiChannel];
@@ -351,8 +382,8 @@ void RSound::processChannelFadeOuts() {
_fadeOutCounter = _fadeOutSpeed;
- for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i)
- Channel_processFadeOut(&_channels[i]);
+ for (int i = 1; i <= RSOUND_CHANNEL_COUNT; ++i)
+ Channel_processFadeOut(getChannel(i));
}
void RSound::setFadeOutSpeed(int fadeOutSpeed) {
@@ -394,16 +425,13 @@ void RSound::Channel_processTick(Channel *channel) {
if (note == 0 || delta == 0) {
Channel_turnOffActiveNotes(channel);
} else {
+ // In the original code this calculation can result in underflow
+ // of the duration, which is a byte value. This actually happens
+ // during playback of several tracks.
+ // Assignment to the noteDurationCounter byte field
+ // results in the same behavior as the original code,
+ // f.e. duration -1 becomes counter value 0xFF.
int duration = delta - channel->_noteDurationOffset;
- if (duration < 0) {
- // In the original code this calculation can result in underflow
- // of the duration, which is a byte value. Not sure if this
- // actually happens in any of the sound data.
- warning("Calculated note duration is negative");
- // Assignment to the noteDurationCounter byte field should
- // result in the same behavior as the original code, i.e.
- // duration -1 becomes counter value 0xFF.
- }
channel->_noteDurationCounter = duration;
// If the note duration offset is 0 or positive, the notes played by
@@ -718,8 +746,8 @@ void RSound::Channel_processTick(Channel *channel) {
}
void RSound::processTickAllChannels() {
- for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i)
- Channel_processTick(&_channels[i]);
+ for (int i = 1; i <= RSOUND_CHANNEL_COUNT; ++i)
+ Channel_processTick(getChannel(i));
}
void RSound::processTick() {
@@ -745,18 +773,20 @@ void RSound::processTick() {
void RSound::resetChannelRange(int firstChannel, int lastChannel, bool includeChannel9) {
_ticksProcessingDisabled = true;
- for (int i = firstChannel - 1; i < lastChannel; ++i) {
- _channels[i]._deltaCounter = 0;
- _channels[i]._pitchSlideStepSize = 0;
- _channels[i]._volumeFadeStepSize = 0;
- _channels[i]._panningSweepStepSize = 0;
+ for (int i = firstChannel; i <= lastChannel; ++i) {
+ Channel *chan = getChannel(i);
+ chan->_deltaCounter = 0;
+ chan->_pitchSlideStepSize = 0;
+ chan->_volumeFadeStepSize = 0;
+ chan->_panningSweepStepSize = 0;
}
if (lastChannel <= 8 && includeChannel9) {
- _channels[8]._deltaCounter = 0;
- _channels[8]._pitchSlideStepSize = 0;
- _channels[8]._volumeFadeStepSize = 0;
- _channels[8]._panningSweepStepSize = 0;
+ Channel *chan = getChannel(9);
+ chan->_deltaCounter = 0;
+ chan->_pitchSlideStepSize = 0;
+ chan->_volumeFadeStepSize = 0;
+ chan->_panningSweepStepSize = 0;
}
_ticksProcessingDisabled = false;
@@ -821,8 +851,8 @@ int RSound::command0() {
int RSound::command1() {
setFadeOutSpeed(1);
- for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i)
- _channels[i].setFadeOut(true);
+ for (int i = 1; i <= RSOUND_CHANNEL_COUNT; ++i)
+ getChannel(i)->setFadeOut(true);
return 0;
}
@@ -842,9 +872,9 @@ int RSound::command2() {
int RSound::command3() {
// Start fade-out to stop for channels 1-4 and 9.
setFadeOutSpeed(1);
- for (int i = 0; i < 4; ++i)
- _channels[i].setFadeOut(true);
- _channels[8].setFadeOut(true);
+ for (int i = 1; i <= 4; ++i)
+ getChannel(i)->setFadeOut(true);
+ getChannel(9)->setFadeOut(true);
return 0;
}
@@ -862,8 +892,8 @@ int RSound::command4() {
int RSound::command5() {
// Start fade-out to stop for channels 5-8.
setFadeOutSpeed(1);
- for (int i = 4; i < 8; ++i)
- _channels[i].setFadeOut(true);
+ for (int i = 5; i <= 8; ++i)
+ getChannel(i)->setFadeOut(true);
return 0;
}
@@ -876,8 +906,8 @@ int RSound::command6() {
}
int RSound::command7() {
- for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i)
- unmuteChannel(_channels[i]._midiChannel, _channels[i]._volume);
+ for (int i = 1; i <= RSOUND_CHANNEL_COUNT; ++i)
+ unmuteChannel(i, getChannel(i)->_volume);
_mute = false;
_ticksProcessingDisabled = false;
return 0;
@@ -885,8 +915,8 @@ int RSound::command7() {
int RSound::command8() {
int result = 0;
- for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i)
- result |= _channels[i]._deltaCounter;
+ for (int i = 1; i <= RSOUND_CHANNEL_COUNT; ++i)
+ result |= getChannel(i)->_deltaCounter;
return result;
}
diff --git a/engines/mads/nebular/sound/rsound.h b/engines/mads/nebular/sound/rsound.h
index cee08d24970..64a17a2a848 100644
--- a/engines/mads/nebular/sound/rsound.h
+++ b/engines/mads/nebular/sound/rsound.h
@@ -78,7 +78,6 @@ enum RSoundFadeCheckMode {
*/
class Channel {
public:
- RSound *_owner = nullptr;
byte _midiChannel = 0; // 1-9: the MIDI channel to which the data in this struct pertains
byte _deltaCounter = 0; // number of ticks until the next event occurs; loaded from the delta byte of a note or chord event
@@ -128,12 +127,6 @@ public:
* volume fade triggered by event 0xF8.
*/
void setFadeOut(bool fadeOut);
-
- /**
- * Loads new sound data into the channel, starts playback by setting
- * _deltaCounter to 1 and resets pitch bend to center on the MT-32.
- */
- void playData(byte *soundData);
};
/**
@@ -208,6 +201,12 @@ private:
void processChannelFadeOuts();
void Channel_processFadeOut(Channel *channel);
+ /**
+ * Loads new sound data into the channel, starts playback by setting
+ * _deltaCounter to 1 and resets pitch bend to center on the MT-32.
+ */
+ void Channel_playData(Channel *channel, byte *soundData);
+
/**
* Sends Note-Off (velocity 0) for all active notes on the
* given channel's MIDI channel, then clears the active note table for it.
@@ -247,32 +246,70 @@ protected:
void resultCheck();
/**
- * Play the specified sound using any free channel from 5 to 8.
- * Returns the channel that was used (or nullptr if none was free),
- * since some commands poke the just-loaded channel's loop pointer
- * directly afterward.
+ * Returns a pointer to the channel data corresponding to the
+ * specified MIDI channel.
+ */
+ Channel *getChannel(byte channel);
+
+ /*
+ * MIDI channels are allocated to sound data either statically or
+ * dynamically. Channels 1-4 and 9 are usually statically allocated,
+ * where the command will specify the channel to use for loading the
+ * sound data using playSoundStatic. Because channel 9 is the rhythm
+ * channel and sound data must be specifically written for this
+ * channel, it can only be allocated statically.
+ * Channels 5-8 are usually dynamically allocated. The command will
+ * use playSoundDynamic to look for a free channel and then load the
+ * sound data into that channel.
+ * Usually, commands will load music statically into channels 1-4 and 9,
+ * and SFX dynamically into channels 5-8. However, there are many
+ * exceptions.
*/
- Channel *playSoundCh5To8(int offset);
/**
- * Play the specified sound using any free channel from 1 to 8.
+ * Play the specified sound by statically allocating the specified
+ * channel. This will unload any sound already playing on the
+ * channel.
*/
- Channel *playSoundCh1To8(int offset);
+ Channel *playSoundStatic(int offset, byte channel);
+ Channel *playSoundStatic(byte *soundData, byte channel);
/**
- * Allocates a MIDI channel, loads the specified sound data into it
- * and starts playback. Allocation will look for a free channel in
- * the range starting with the specified channel (default 5) and
- * ending with channel 8. If no suitable channel could be found,
- * nullptr is returned and the sound data is not played.
+ * Play the specified sound by allocating one of the free dynamic
+ * channels. Returns the channel that was used (or nullptr if none
+ * was free), since some commands poke the just-loaded channel's
+ * loop pointer directly afterward.
+ */
+ Channel *playSoundDynamic(int offset);
+
+ /**
+ * Play the specified sound by allocating any free melodic channel,
+ * dynamic or static.
+ */
+ Channel *playSoundAnyChannel(int offset);
+
+ /**
+ * Dynamically allocates a melodic channel, loads the specified sound
+ * data into it and starts playback. Allocation will look for a
+ * free channel in the range starting with the specified channel
+ * (default 5) and ending with channel 8. If no suitable channel
+ * could be found, nullptr is returned and the sound data is not
+ * played.
*/
Channel *allocateAndPlay(byte *pData, int startingChannel = 5);
/**
- * Checks to see whether the given block of data is already loaded into a channel.
+ * Checks to see whether the given block of data is already loaded
+ * into a channel and being played.
*/
bool isSoundPlaying(byte *pData);
+ /**
+ * Checks to see whether the given block of data is already loaded
+ * into the specified channel and being played.
+ */
+ bool isSoundPlaying(byte channel, byte *pData);
+
uint16 generateRandomNumber();
// ---- Low-level MIDI send helpers -------------------------------
@@ -292,6 +329,12 @@ protected:
*/
void sendMidiChannelReset(int first, int last);
+ /**
+ * Sets the volume for a channel. The value is set on the channel data
+ * and it is sent out to the MT-32.
+ */
+ void setChannelVolume(byte channel, byte volume);
+
/**
* Sends a single SysEx message: bytes from pData up to (but not
* including) a 0xFF terminator, via the MT32GM MIDI driver. Returns a
diff --git a/engines/mads/nebular/sound/rsound_nebular.cpp b/engines/mads/nebular/sound/rsound_nebular.cpp
index 024157a727b..eb974dad174 100644
--- a/engines/mads/nebular/sound/rsound_nebular.cpp
+++ b/engines/mads/nebular/sound/rsound_nebular.cpp
@@ -35,7 +35,7 @@ RSoundDemo::RSoundDemo(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver, const
void RSoundDemo::startVoice(int channelIndex, int sequenceOffset) {
assert(channelIndex >= 0 && channelIndex < RSOUND_CHANNEL_COUNT);
- _channels[channelIndex].playData(loadData(sequenceOffset));
+ playSoundStatic(sequenceOffset, channelIndex + 1);
}
int RSoundDemo::startVoiceInRange(int sequenceOffset, int firstChannel,
@@ -129,62 +129,56 @@ int RSound1::clampParam() {
return (_commandParam > 0x40) ? _commandParam - 0x40 : 0;
}
-void RSound1::method1() {
+void RSound1::playCommand11_12_13SharedChannels() {
byte *pData = loadData(0x1166);
if (!isSoundPlaying(pData)) {
command1();
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0x13BC));
- _channels[2].playData(loadData(0x155C));
- _channels[3].playData(loadData(0x15D8));
+ playSoundStatic(pData, 1);
+ playSoundStatic(0x13BC, 2);
+ playSoundStatic(0x155C, 3);
+ playSoundStatic(0x15D8, 4);
}
}
int RSound1::command9() {
- playSoundCh5To8(0xAD4);
+ playSoundDynamic(0xAD4);
return 0;
}
int RSound1::command10() {
byte *pData = loadData(0xCE4);
if (!isSoundPlaying(pData)) {
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0xD18));
- _channels[2].playData(loadData(0xE9C));
- _channels[3].playData(loadData(0xEE8));
+ playSoundStatic(pData, 1);
+ playSoundStatic(0xD18, 2);
+ playSoundStatic(0xE9C, 3);
+ playSoundStatic(0xEE8, 4);
}
return 0;
}
int RSound1::command11() {
- method1();
- _channels[0]._volume = 0;
- sendVolume(1, _channels[0]._volume);
- _channels[1]._volume = 0;
- sendVolume(2, _channels[1]._volume);
+ playCommand11_12_13SharedChannels();
+ setChannelVolume(1, 0);
+ setChannelVolume(2, 0);
return 0;
}
int RSound1::command12() {
- method1();
- _channels[0]._volume = 80;
- sendVolume(1, _channels[0]._volume);
- _channels[1]._volume = 0;
- sendVolume(2, _channels[1]._volume);
+ playCommand11_12_13SharedChannels();
+ setChannelVolume(1, 80);
+ setChannelVolume(2, 0);
return 0;
}
int RSound1::command13() {
- method1();
- _channels[0]._volume = 80;
- sendVolume(1, _channels[0]._volume);
- _channels[1]._volume = 80;
- sendVolume(2, _channels[1]._volume);
+ playCommand11_12_13SharedChannels();
+ setChannelVolume(1, 80);
+ setChannelVolume(2, 80);
return 0;
}
int RSound1::command14() {
- playSoundCh5To8(0x16C2);
+ playSoundDynamic(0x16C2);
return 0;
}
@@ -192,63 +186,63 @@ int RSound1::command15() {
byte *pData = loadData(0xF3A);
if (!isSoundPlaying(pData)) {
command1();
- _channels[4].playData(pData);
- _channels[5].playData(loadData(0x102A));
- _channels[6].playData(loadData(0x110E));
+ playSoundStatic(pData, 5);
+ playSoundStatic(0x102A, 6);
+ playSoundStatic(0x110E, 7);
}
return 0;
}
int RSound1::command16() {
- playSoundCh5To8(0xADE);
+ playSoundDynamic(0xADE);
return 0;
}
int RSound1::command17() {
- playSoundCh5To8(0xAE8);
+ playSoundDynamic(0xAE8);
return 0;
}
int RSound1::command18() {
- playSoundCh5To8(0xAF2);
+ playSoundDynamic(0xAF2);
return 0;
}
int RSound1::command19() {
command1();
- playSoundCh5To8(0xB04);
+ playSoundDynamic(0xB04);
return 0;
}
int RSound1::command20() {
- playSoundCh5To8(0xB5E);
+ playSoundDynamic(0xB5E);
return 0;
}
int RSound1::command21() {
- playSoundCh5To8(0xB4C);
+ playSoundDynamic(0xB4C);
return 0;
}
int RSound1::command22() {
- playSoundCh5To8(0xB6E);
+ playSoundDynamic(0xB6E);
return 0;
}
int RSound1::command23() {
byte *pData = loadData(0xB7A);
pData[6] ^= 0x1F;
- playSoundCh5To8(0xB7A);
+ playSoundDynamic(0xB7A);
return 0;
}
int RSound1::command24() {
- playSoundCh5To8(0xB84);
+ playSoundDynamic(0xB84);
return 0;
}
int RSound1::command25() {
- playSoundCh5To8(0xB8E);
+ playSoundDynamic(0xB8E);
return 0;
}
@@ -258,7 +252,7 @@ int RSound1::command26() {
pData[8] = v1;
int v2 = clampParam() + 64;
pData[5] = v2;
- _channels[7].playData(pData);
+ playSoundStatic(pData, 8);
return 0;
}
@@ -268,12 +262,12 @@ int RSound1::command27() {
pData[8] = v1;
int v2 = clampParam() + 64;
pData[5] = v2;
- _channels[7].playData(pData);
+ playSoundStatic(pData, 8);
return 0;
}
int RSound1::command28() {
- playSoundCh5To8(0xB9E);
+ playSoundDynamic(0xB9E);
return 0;
}
@@ -282,7 +276,7 @@ int RSound1::command29() {
int v = (clampParam() >> 1) + 32;
pData[0xB] = v;
if (!isSoundPlaying(pData))
- playSoundCh5To8(0xC6C);
+ playSoundDynamic(0xC6C);
return 0;
}
@@ -291,12 +285,12 @@ int RSound1::command30() {
int v = clampParam() + 63;
pData[0xB] = v;
if (!isSoundPlaying(pData))
- playSoundCh1To8(0xC80);
+ playSoundAnyChannel(0xC80);
return 0;
}
int RSound1::command31() {
- playSoundCh5To8(0xBBE);
+ playSoundDynamic(0xBBE);
return 0;
}
@@ -306,13 +300,13 @@ int RSound1::command32() {
pData[0xB] = pData[0x17] = half + 68;
pData[0x11] = pData[0x1D] = half + 20;
if (!isSoundPlaying(pData))
- playSoundCh1To8(0xC96);
+ playSoundAnyChannel(0xC96);
return 0;
}
int RSound1::command33() {
- playSoundCh5To8(0xBD0);
- playSoundCh5To8(0xBDA);
+ playSoundDynamic(0xBD0);
+ playSoundDynamic(0xBDA);
return 0;
}
@@ -321,19 +315,19 @@ int RSound1::command34() {
int v = (generateRandomNumber() & 12) + 45;
pData[9] = v;
pData[0x10] = v + 36;
- playSoundCh5To8(0xBE8);
+ playSoundDynamic(0xBE8);
return 0;
}
int RSound1::command35() {
- playSoundCh5To8(0xBFC);
- playSoundCh5To8(0xC0E);
- playSoundCh5To8(0xC20);
+ playSoundDynamic(0xBFC);
+ playSoundDynamic(0xC0E);
+ playSoundDynamic(0xC20);
return 0;
}
int RSound1::command36() {
- playSoundCh5To8(0xC3E);
+ playSoundDynamic(0xC3E);
return 0;
}
@@ -342,35 +336,35 @@ int RSound1::command37() {
int r = generateRandomNumber() & 15;
pData[6] = r + 42;
pData[3] = 62 - (r << 1);
- playSoundCh5To8(0xC4C);
+ playSoundDynamic(0xC4C);
return 0;
}
int RSound1::command38() {
- playSoundCh1To8(0xC56);
- playSoundCh5To8(0xC60);
+ playSoundAnyChannel(0xC56);
+ playSoundDynamic(0xC60);
return 0;
}
int RSound1::command39() {
byte *pData = loadData(0x1818);
if (!isSoundPlaying(pData)) {
- _channels[4].playData(pData);
- _channels[5].playData(loadData(0x1848));
- _channels[6].playData(loadData(0x1874));
- _channels[7].playData(loadData(0x18B4));
- _channels[8].playData(loadData(0x18CE));
+ playSoundStatic(pData, 5);
+ playSoundStatic(0x1848, 6);
+ playSoundStatic(0x1874, 7);
+ playSoundStatic(0x18B4, 8);
+ playSoundStatic(0x18CE, 9);
}
return 0;
}
int RSound1::command40() {
- playSoundCh5To8(0xC34);
+ playSoundDynamic(0xC34);
return 0;
}
int RSound1::command41() {
- playSoundCh5To8(0xCD6);
+ playSoundDynamic(0xCD6);
return 0;
}
@@ -386,7 +380,7 @@ byte RSoundDemo1::adjustedCommandParam() const {
return value > 0x40 ? value - 0x40 : 0;
}
-void RSoundDemo1::startCommand111213() {
+void RSoundDemo1::playCommand11_12_13CommonChannels() {
if (isSequenceActive(0x1586))
return;
@@ -450,17 +444,17 @@ int RSoundDemo1::command(int commandId, int param) {
}
break;
case 11:
- startCommand111213();
+ playCommand11_12_13CommonChannels();
setVoiceVolume(0, 0x00);
setVoiceVolume(1, 0x00);
break;
case 12:
- startCommand111213();
+ playCommand11_12_13CommonChannels();
setVoiceVolume(0, 0x50);
setVoiceVolume(1, 0x00);
break;
case 13:
- startCommand111213();
+ playCommand11_12_13CommonChannels();
setVoiceVolume(0, 0x50);
setVoiceVolume(1, 0x50);
break;
@@ -590,7 +584,7 @@ int RSoundDemo1::command(int commandId, int param) {
/*-----------------------------------------------------------------------*/
-const uint16 RSound2::_table1[16] = {
+const uint16 RSound2::_command18RandomSfx[16] = {
0x3234, 0x3250, 0x326A, 0x3284, 0x329E, 0x32D6, 0x3304, 0x333C,
0x3352, 0x3378, 0x33B6, 0x33D0, 0x33EA, 0x3404, 0x341E, 0x343E
};
@@ -623,12 +617,10 @@ int RSound2::command(int commandId, int param) {
}
int RSound2::command5() {
- _pitchCycleCounter = 47;
- _channels[3].setFadeOut(true);
- _channels[4].setFadeOut(true);
- _channels[5].setFadeOut(true);
- _channels[6].setFadeOut(true);
- _channels[7].setFadeOut(true);
+ _volumeCycleCounter = 47;
+ for (int i = 4; i <= 8; i++) {
+ getChannel(i)->setFadeOut(true);
+ }
return 0;
}
@@ -636,9 +628,9 @@ int RSound2::command9() {
byte *pData = loadData(0x103C);
if (!isSoundPlaying(pData)) {
command1();
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0x11B2));
- _channels[8].playData(loadData(0x127E));
+ playSoundStatic(pData, 1);
+ playSoundStatic(0x11B2, 2);
+ playSoundStatic(0x127E, 9);
}
return 0;
}
@@ -647,8 +639,8 @@ int RSound2::command10() {
byte *pData = loadData(0x132E);
if (!isSoundPlaying(pData)) {
command1();
- _channels[2].playData(pData);
- _channels[8].playData(loadData(0x1384));
+ playSoundStatic(pData, 3);
+ playSoundStatic(0x1384, 9);
}
return 0;
}
@@ -657,29 +649,29 @@ int RSound2::command11() {
byte *pData = loadData(0x1548);
if (!isSoundPlaying(pData)) {
command1();
- _channels[2].playData(pData);
- _channels[8].playData(loadData(0x1648));
+ playSoundStatic(pData, 3);
+ playSoundStatic(0x1648, 9);
}
return 0;
}
int RSound2::command12() {
byte *pData = loadData(0xE52);
- _pitchCycleCounter += 16;
- pData[3] = _pitchCycleCounter & 0x7F;
- playSoundCh5To8(0xE52);
+ _volumeCycleCounter += 16;
+ pData[3] = _volumeCycleCounter & 0x7F;
+ playSoundDynamic(0xE52);
return 0;
}
int RSound2::command13() {
- playSoundCh5To8(0xE5C);
- playSoundCh5To8(0xE66);
+ playSoundDynamic(0xE5C);
+ playSoundDynamic(0xE66);
return 0;
}
int RSound2::command14() {
- playSoundCh5To8(0xE70);
- playSoundCh5To8(0xE8A);
+ playSoundDynamic(0xE70);
+ playSoundDynamic(0xE8A);
return 0;
}
@@ -687,10 +679,10 @@ int RSound2::command15() {
byte *pData = loadData(0x1DFC);
if (!isSoundPlaying(pData)) {
command1();
- playSoundCh1To8(0x1DFC);
- playSoundCh1To8(0x222E);
- playSoundCh1To8(0x2648);
- _channels[8].playData(loadData(0x26A2));
+ playSoundAnyChannel(0x1DFC);
+ playSoundAnyChannel(0x222E);
+ playSoundAnyChannel(0x2648);
+ playSoundStatic(0x26A2, 9);
}
return 0;
}
@@ -699,12 +691,12 @@ int RSound2::command16() {
byte *pData = loadData(0x3456);
if (!isSoundPlaying(pData)) {
command1();
- playSoundCh1To8(0x3456);
- playSoundCh1To8(0x3572);
- playSoundCh1To8(0x367E);
- playSoundCh1To8(0x37C6);
- playSoundCh1To8(0x39AE);
- playSoundCh1To8(0x3A3A);
+ playSoundAnyChannel(0x3456);
+ playSoundAnyChannel(0x3572);
+ playSoundAnyChannel(0x367E);
+ playSoundAnyChannel(0x37C6);
+ playSoundAnyChannel(0x39AE);
+ playSoundAnyChannel(0x3A3A);
}
return 0;
}
@@ -712,20 +704,20 @@ int RSound2::command16() {
int RSound2::command17() {
byte *pData = loadData(0x3AC0);
if (!isSoundPlaying(pData)) {
- playSoundCh5To8(0x3AC0);
- playSoundCh5To8(0x3C70);
- playSoundCh5To8(0x3E16);
- playSoundCh5To8(0x3FBE);
+ playSoundDynamic(0x3AC0);
+ playSoundDynamic(0x3C70);
+ playSoundDynamic(0x3E16);
+ playSoundDynamic(0x3FBE);
}
return 0;
}
int RSound2::command18() {
- if (_channels[7]._deltaCounter)
+ if (getChannel(8)->_deltaCounter > 0)
return 0;
- int idx = (generateRandomNumber() & 30) >> 1;
- _channels[7].playData(loadData(_table1[idx]));
+ int idx = (generateRandomNumber() & 0x1E) >> 1;
+ playSoundStatic(_command18RandomSfx[idx], 8);
return 0;
}
@@ -733,64 +725,64 @@ int RSound2::command19() {
byte *pData = loadData(0x2A64);
if (!isSoundPlaying(pData)) {
command1();
- playSoundCh1To8(0x2A64);
- playSoundCh1To8(0x2BE2);
- playSoundCh1To8(0x2DAC);
- playSoundCh1To8(0x2ECE);
- playSoundCh1To8(0x3026);
- playSoundCh1To8(0x30C2);
+ playSoundAnyChannel(0x2A64);
+ playSoundAnyChannel(0x2BE2);
+ playSoundAnyChannel(0x2DAC);
+ playSoundAnyChannel(0x2ECE);
+ playSoundAnyChannel(0x3026);
+ playSoundAnyChannel(0x30C2);
}
return 0;
}
int RSound2::command20() {
- playSoundCh5To8(0xF1E);
- playSoundCh5To8(0xF1E);
- playSoundCh5To8(0xF1E);
- playSoundCh5To8(0xF1E);
+ playSoundDynamic(0xF1E);
+ playSoundDynamic(0xF1E);
+ playSoundDynamic(0xF1E);
+ playSoundDynamic(0xF1E);
return 0;
}
int RSound2::command21() {
- playSoundCh5To8(0xF58);
+ playSoundDynamic(0xF58);
return 0;
}
int RSound2::command22() {
- playSoundCh5To8(0xF44);
+ playSoundDynamic(0xF44);
return 0;
}
int RSound2::command23() {
- playSoundCh5To8(0xEBA);
+ playSoundDynamic(0xEBA);
return 0;
}
int RSound2::command24() {
- playSoundCh5To8(0xEB0);
+ playSoundDynamic(0xEB0);
return 0;
}
int RSound2::command25() {
- playSoundCh5To8(0xEA6);
+ playSoundDynamic(0xEA6);
return 0;
}
int RSound2::command26() {
- playSoundCh5To8(0xF4E);
+ playSoundDynamic(0xF4E);
return 0;
}
int RSound2::command27() {
- Channel *ch = playSoundCh5To8(0xFAC);
- if (ch)
+ Channel *ch = playSoundDynamic(0xFAC);
+ if (ch != nullptr)
ch->_innerLoopStart = loadData(0xFB8);
- ch = playSoundCh5To8(0xFB2);
- if (ch)
+ ch = playSoundDynamic(0xFB2);
+ if (ch != nullptr)
ch->_innerLoopStart = loadData(0xFB8);
- playSoundCh5To8(0xFB8);
+ playSoundDynamic(0xFB8);
return 0;
}
@@ -803,58 +795,58 @@ int RSound2::command28() {
pData[8] = v2;
int v3 = v2 + 0x0C;
pData[0xA] = v3;
- playSoundCh5To8(0xEDA);
+ playSoundDynamic(0xEDA);
return 0;
}
int RSound2::command29() {
- playSoundCh1To8(0xF80);
+ playSoundAnyChannel(0xF80);
return 0;
}
int RSound2::command30() {
- playSoundCh5To8(0xF14);
+ playSoundDynamic(0xF14);
byte *pData = loadData(0xF0A);
pData[3] = 40;
- playSoundCh5To8(0xF0A);
+ playSoundDynamic(0xF0A);
return 0;
}
int RSound2::command31() {
byte *pData = loadData(0xF0A);
pData[3] = 0x18;
- playSoundCh5To8(0xF0A);
+ playSoundDynamic(0xF0A);
return 0;
}
int RSound2::command32() {
- playSoundCh5To8(0xEC4);
+ playSoundDynamic(0xEC4);
return 0;
}
int RSound2::command33() {
- playSoundCh5To8(0xED0);
+ playSoundDynamic(0xED0);
return 0;
}
int RSound2::command34() {
- playSoundCh5To8(0xEE8);
+ playSoundDynamic(0xEE8);
return 0;
}
int RSound2::command35() {
- playSoundCh5To8(0xEF8);
+ playSoundDynamic(0xEF8);
return 0;
}
int RSound2::command36() {
- playSoundCh5To8(0xFF4);
- playSoundCh5To8(0x1008);
+ playSoundDynamic(0xFF4);
+ playSoundDynamic(0x1008);
return 0;
}
int RSound2::command37() {
- playSoundCh5To8(0xFCC);
+ playSoundDynamic(0xFCC);
return 0;
}
@@ -862,39 +854,39 @@ int RSound2::command38() {
byte *pData = loadData(0x2B0E);
if (!isSoundPlaying(pData)) {
command1();
- playSoundCh1To8(0x2B0E);
- playSoundCh1To8(0x2CD0);
- playSoundCh1To8(0x2E46);
- playSoundCh1To8(0x2F7C);
- playSoundCh1To8(0x3074);
- playSoundCh1To8(0x317E);
+ playSoundAnyChannel(0x2B0E);
+ playSoundAnyChannel(0x2CD0);
+ playSoundAnyChannel(0x2E46);
+ playSoundAnyChannel(0x2F7C);
+ playSoundAnyChannel(0x3074);
+ playSoundAnyChannel(0x317E);
}
return 0;
}
int RSound2::command39() {
- playSoundCh5To8(0xFDA);
+ playSoundDynamic(0xFDA);
return 0;
}
int RSound2::command40() {
- playSoundCh5To8(0xFE6);
+ playSoundDynamic(0xFE6);
return 0;
}
int RSound2::command41() {
- playSoundCh1To8(0xF62);
+ playSoundAnyChannel(0xF62);
return 0;
}
int RSound2::command42() {
- playSoundCh5To8(0xF92);
+ playSoundDynamic(0xF92);
return 0;
}
int RSound2::command43() {
- playSoundCh5To8(0x1018);
- playSoundCh5To8(0x102A);
+ playSoundDynamic(0x1018);
+ playSoundDynamic(0x102A);
return 0;
}
@@ -907,13 +899,13 @@ const RSound3::CommandPtr RSound3::_commandList[61] = {
&RSound3::nullCommand, &RSound3::command13, &RSound3::command14, &RSound3::command15,
&RSound3::command16, &RSound3::command17, &RSound3::command18, &RSound3::command19,
&RSound3::command20, &RSound3::command21, &RSound3::command22, &RSound3::command23,
- &RSound3::command24, &RSound3::command25, &RSound3::command26, &RSound3::command27x42,
+ &RSound3::command24, &RSound3::command25, &RSound3::command26, &RSound3::command27_42,
&RSound3::command28, &RSound3::command29, &RSound3::command30, &RSound3::command31,
&RSound3::command32, &RSound3::command33, &RSound3::command34, &RSound3::command35,
&RSound3::command36, &RSound3::command37, &RSound3::command38, &RSound3::command39,
- &RSound3::command40, &RSound3::command41, &RSound3::command27x42, &RSound3::command43,
- &RSound3::command44, &RSound3::command45, &RSound3::command46, &RSound3::command47x49,
- &RSound3::command48, &RSound3::command47x49, &RSound3::command50, &RSound3::command51,
+ &RSound3::command40, &RSound3::command41, &RSound3::command27_42, &RSound3::command43,
+ &RSound3::command44, &RSound3::command45, &RSound3::command46, &RSound3::command47_49,
+ &RSound3::command48, &RSound3::command47_49, &RSound3::command50, &RSound3::command51,
&RSound3::nullCommand, &RSound3::nullCommand, &RSound3::nullCommand, &RSound3::nullCommand,
&RSound3::nullCommand, &RSound3::command57, &RSound3::nullCommand, &RSound3::command59,
&RSound3::command60
@@ -932,35 +924,10 @@ int RSound3::command(int commandId, int param) {
return (this->*_commandList[commandId])();
}
-Channel *RSound3::method1(int offset, byte value) {
+Channel *RSound3::patchAndPlaySound(int offset, byte value) {
byte *pData = loadData(offset);
pData[5] = value;
- return playSoundCh5To8(offset);
-}
-
-void RSound3::resetUpperChannelsTail() {
- setFadeOutSpeed(1);
- _channels[4].setFadeOut(true);
- _channels[5].setFadeOut(true);
- _channels[6].setFadeOut(true);
- _channels[7].setFadeOut(true);
-}
-
-int RSound3::command1() {
- command3();
- resetUpperChannelsTail();
- return 0;
-}
-
-int RSound3::command3() {
- return RSound::command3();
-}
-
-int RSound3::command5() {
- // The native driver performs an unused active-sequence probe before
- // unconditionally entering the shared upper-channel reset tail.
- resetUpperChannelsTail();
- return 0;
+ return playSoundDynamic(offset);
}
int RSound3::command9() {
@@ -973,11 +940,11 @@ int RSound3::command10() {
byte *pData = loadData(0x14FE);
if (!isSoundPlaying(pData)) {
command1();
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0x1630));
- _channels[2].playData(loadData(0x186E));
- _channels[3].playData(loadData(0x1A68));
- _channels[8].playData(loadData(0x1AA6));
+ playSoundStatic(pData, 1);
+ playSoundStatic(0x1630, 2);
+ playSoundStatic(0x186E, 3);
+ playSoundStatic(0x1A68, 4);
+ playSoundStatic(0x1AA6, 9);
}
return 0;
}
@@ -992,64 +959,61 @@ void RSound3::setVariantByte(byte value) {
int RSound3::command11() {
if (!isSoundPlaying(loadData(0x1AE6))) {
setVariantByte(0x64);
- _channels[0].playData(loadData(0x1AE6));
- _channels[1].playData(loadData(0x1E00));
- _channels[2].playData(loadData(0x1E66));
- _channels[3].playData(loadData(0x204A));
- _channels[4].playData(loadData(0x229C));
- _channels[5].playData(loadData(0x2748));
- _channels[6].playData(loadData(0x2C56));
+ playSoundStatic(0x1AE6, 1);
+ playSoundStatic(0x1E00, 2);
+ playSoundStatic(0x1E66, 3);
+ playSoundStatic(0x204A, 4);
+ playSoundStatic(0x229C, 5);
+ playSoundStatic(0x2748, 6);
+ playSoundStatic(0x2C56, 7);
}
return 0;
}
int RSound3::command13() {
command1();
- playSoundCh1To8(0x1364);
- playSoundCh1To8(0x1364);
- playSoundCh1To8(0x1364);
- playSoundCh1To8(0x1364);
- playSoundCh1To8(0x1364);
+ playSoundAnyChannel(0x1364);
+ playSoundAnyChannel(0x1364);
+ playSoundAnyChannel(0x1364);
+ playSoundAnyChannel(0x1364);
+ playSoundAnyChannel(0x1364);
return 0;
}
int RSound3::command14() {
- _channels[0].playData(loadData(0x32DC));
- _channels[1].playData(loadData(0x32FC));
- _channels[2].playData(loadData(0x331C));
- _channels[3].playData(loadData(0x333E));
- _channels[4].playData(loadData(0x335C));
- _channels[5].playData(loadData(0x339E));
- _channels[6].playData(loadData(0x33DE));
- _channels[7].playData(loadData(0x341E));
+ playSoundStatic(0x32DC, 1);
+ playSoundStatic(0x32FC, 2);
+ playSoundStatic(0x331C, 3);
+ playSoundStatic(0x333E, 4);
+ playSoundStatic(0x335C, 5);
+ playSoundStatic(0x339E, 6);
+ playSoundStatic(0x33DE, 7);
+ playSoundStatic(0x341E, 8);
return 0;
}
void RSound3::sendDualVolume(byte volume) {
- _channels[0]._volume = volume;
- sendVolume(1, volume);
- _channels[0]._volume = volume;
- sendVolume(2, volume);
+ setChannelVolume(1, volume);
+ // The original code sets volume on the channel data for channel 1 (again),
+ // but sends it to the MT-32 on channel 2. Most likely a bug.
+ setChannelVolume(2, volume);
}
int RSound3::command15() {
setVariantByte(0x60);
sendDualVolume(0x60);
- if (_channels[3]._deltaCounter && _channels[3]._soundData == loadData(0x204A)) {
- _channels[2]._fadeOutActive = true;
- _channels[3]._fadeOutActive = true;
- _channels[4]._fadeOutActive = true;
- _channels[5]._fadeOutActive = true;
- _channels[6]._fadeOutActive = true;
- _channels[7]._fadeOutActive = true;
+ if (isSoundPlaying(4, loadData(0x204A))) {
+ for (int i = 3; i <= 8; i++) {
+ getChannel(i)->_fadeOutActive = true;
+ }
setFadeOutSpeed(1);
return 0;
}
command1();
- _channels[0].playData(loadData(0x1AE6));
- _channels[1].playData(loadData(0x1E00));
+ playSoundStatic(0x1AE6, 1);
+ playSoundStatic(0x1E00, 2);
return 0;
}
@@ -1059,19 +1023,19 @@ int RSound3::command16() {
if (_command16AltFlag) {
byte *pData = loadData(0x345E);
if (!isSoundPlaying(pData)) {
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0x364C));
- _channels[2].playData(loadData(0x3806));
- _channels[3].playData(loadData(0x399E));
+ playSoundStatic(pData, 1);
+ playSoundStatic(0x364C, 2);
+ playSoundStatic(0x3806, 3);
+ playSoundStatic(0x399E, 4);
}
} else {
byte *pData = loadData(0x3B26);
if (!isSoundPlaying(pData)) {
command1();
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0x3BD8));
- _channels[2].playData(loadData(0x3CF8));
- _channels[3].playData(loadData(0x3E46));
+ playSoundStatic(pData, 1);
+ playSoundStatic(0x3BD8, 2);
+ playSoundStatic(0x3CF8, 3);
+ playSoundStatic(0x3E46, 4);
}
}
return 0;
@@ -1081,62 +1045,62 @@ int RSound3::command17() {
byte *pData = loadData(0x3F5C);
if (!isSoundPlaying(pData)) {
command1();
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0x4022));
- _channels[2].playData(loadData(0x41F0));
- _channels[3].playData(loadData(0x42F8));
+ playSoundStatic(pData, 1);
+ playSoundStatic(0x4022, 2);
+ playSoundStatic(0x41F0, 3);
+ playSoundStatic(0x42F8, 4);
}
return 0;
}
int RSound3::command18() {
command1();
- _channels[0].playData(loadData(0x4492));
- _channels[1].playData(loadData(0x45A4));
- _channels[2].playData(loadData(0x46A2));
- _channels[3].playData(loadData(0x47DC));
- _channels[4].playData(loadData(0x49C0));
- _channels[5].playData(loadData(0x4A4E));
+ playSoundStatic(0x4492, 1);
+ playSoundStatic(0x45A4, 2);
+ playSoundStatic(0x46A2, 3);
+ playSoundStatic(0x47DC, 4);
+ playSoundStatic(0x49C0, 5);
+ playSoundStatic(0x4A4E, 6);
return 0;
}
int RSound3::command19() {
if (!isSoundPlaying(loadData(0x1AE6)))
- playSoundCh5To8(0x12B4);
+ playSoundDynamic(0x12B4);
return 0;
}
int RSound3::command20() {
if (!isSoundPlaying(loadData(0x1AE6)))
- playSoundCh5To8(0x1246);
+ playSoundDynamic(0x1246);
return 0;
}
int RSound3::command21() {
if (!isSoundPlaying(loadData(0x1AE6))) {
- playSoundCh5To8(0x1232);
- playSoundCh5To8(0x123C);
+ playSoundDynamic(0x1232);
+ playSoundDynamic(0x123C);
}
return 0;
}
int RSound3::command22() {
- playSoundCh5To8(0x126E);
+ playSoundDynamic(0x126E);
return 0;
}
int RSound3::command23() {
if (!isSoundPlaying(loadData(0x1AE6))) {
- playSoundCh5To8(0x12D2);
- playSoundCh5To8(0x12E0);
+ playSoundDynamic(0x12D2);
+ playSoundDynamic(0x12E0);
}
return 0;
}
int RSound3::command24() {
if (!isSoundPlaying(loadData(0x1AE6))) {
- playSoundCh5To8(0x11E2);
- playSoundCh5To8(0x120A);
+ playSoundDynamic(0x11E2);
+ playSoundDynamic(0x120A);
}
return 0;
}
@@ -1144,162 +1108,162 @@ int RSound3::command24() {
int RSound3::command25() {
// The dispatcher's preceding xor leaves ZF set, so the native jz
// selects 0x25 for both calls.
- method1(0x11A6, 0x25);
- method1(0x11C4, 0x25);
+ patchAndPlaySound(0x11A6, 0x25);
+ patchAndPlaySound(0x11C4, 0x25);
return 0;
}
int RSound3::command26() {
- playSoundCh5To8(0x1252);
+ playSoundDynamic(0x1252);
return 0;
}
-int RSound3::command27x42() {
- playSoundCh5To8(0x14BE);
+int RSound3::command27_42() {
+ playSoundDynamic(0x14BE);
return 0;
}
int RSound3::command28() {
byte *pData = loadData(0x12EE);
- pData[3] = 0x4D;
- playSoundCh5To8(0x12EE);
+ pData[3] = 0x4D; // Medium volume
+ playSoundDynamic(0x12EE);
return 0;
}
int RSound3::command29() {
byte *pData = loadData(0x12EE);
- pData[3] = 0x7F;
- playSoundCh5To8(0x12EE);
+ pData[3] = 0x7F; // High volume
+ playSoundDynamic(0x12EE);
return 0;
}
int RSound3::command30() {
- playSoundCh5To8(0x14B4);
- playSoundCh5To8(0x14AA);
+ playSoundDynamic(0x14B4);
+ playSoundDynamic(0x14AA);
return 0;
}
int RSound3::command31() {
- playSoundCh5To8(0x12F8);
- playSoundCh5To8(0x130E);
+ playSoundDynamic(0x12F8);
+ playSoundDynamic(0x130E);
return 0;
}
int RSound3::command32() {
- playSoundCh5To8(0x1472);
+ playSoundDynamic(0x1472);
return 0;
}
int RSound3::command33() {
- playSoundCh5To8(0x147E);
+ playSoundDynamic(0x147E);
return 0;
}
int RSound3::command34() {
- playSoundCh5To8(0x1488);
+ playSoundDynamic(0x1488);
return 0;
}
int RSound3::command35() {
- playSoundCh5To8(0x1498);
+ playSoundDynamic(0x1498);
return 0;
}
int RSound3::command36() {
- playSoundCh5To8(0x14DA);
- playSoundCh5To8(0x14EE);
+ playSoundDynamic(0x14DA);
+ playSoundDynamic(0x14EE);
return 0;
}
int RSound3::command37() {
- playSoundCh5To8(0x14CC);
+ playSoundDynamic(0x14CC);
return 0;
}
int RSound3::command38() {
- playSoundCh5To8(0x133A);
+ playSoundDynamic(0x133A);
return 0;
}
int RSound3::command39() {
byte *pData = loadData(0x1346);
pData[3] = 77;
- _command3940Toggle ^= 4;
- pData[6] = _command3940Toggle + 0x28;
- playSoundCh5To8(0x1346);
+ _command39_40NoteToggle ^= 4;
+ pData[6] = _command39_40NoteToggle + 0x28;
+ playSoundDynamic(0x1346);
return 0;
}
int RSound3::command40() {
byte *pData = loadData(0x1346);
pData[3] = 47;
- _command3940Toggle ^= 4;
- pData[6] = _command3940Toggle + 0x28;
- playSoundCh5To8(0x1346);
+ _command39_40NoteToggle ^= 4;
+ pData[6] = _command39_40NoteToggle + 0x28;
+ playSoundDynamic(0x1346);
return 0;
}
int RSound3::command41() {
- playSoundCh5To8(0x1184);
+ playSoundDynamic(0x1184);
return 0;
}
int RSound3::command43() {
- playSoundCh5To8(0x1350);
- playSoundCh5To8(0x135A);
+ playSoundDynamic(0x1350);
+ playSoundDynamic(0x135A);
return 0;
}
int RSound3::command44() {
- playSoundCh5To8(0x12A0);
+ playSoundDynamic(0x12A0);
return 0;
}
int RSound3::command45() {
- playSoundCh5To8(0x12AA);
+ playSoundDynamic(0x12AA);
return 0;
}
int RSound3::command46() {
- playSoundCh5To8(0x13AA);
- playSoundCh5To8(0x13C6);
+ playSoundDynamic(0x13AA);
+ playSoundDynamic(0x13C6);
return 0;
}
-int RSound3::command47x49() {
- playSoundCh5To8(0x13E6);
- playSoundCh5To8(0x13FE);
+int RSound3::command47_49() {
+ playSoundDynamic(0x13E6);
+ playSoundDynamic(0x13FE);
return 0;
}
int RSound3::command48() {
- playSoundCh5To8(0x141E);
+ playSoundDynamic(0x141E);
return 0;
}
int RSound3::command50() {
- playSoundCh5To8(0x1436);
- playSoundCh5To8(0x144C);
+ playSoundDynamic(0x1436);
+ playSoundDynamic(0x144C);
return 0;
}
int RSound3::command51() {
- playSoundCh5To8(0x125C);
+ playSoundDynamic(0x125C);
return 0;
}
int RSound3::command57() {
- playSoundCh5To8(0x1466);
+ playSoundDynamic(0x1466);
return 0;
}
int RSound3::command59() {
- playSoundCh5To8(0x1324);
+ playSoundDynamic(0x1324);
return 0;
}
int RSound3::command60() {
- playSoundCh5To8(0x132E);
+ playSoundDynamic(0x132E);
return 0;
}
@@ -1337,28 +1301,40 @@ int RSound4::command(int commandId, int param) {
}
void RSound4::tickCallback() {
- if (!_callbackPeriod)
- return;
- if (--_callbackCounter)
+ if (_callbackPeriod == 0 || --_callbackCounter > 0)
return;
_callbackCounter = _callbackPeriod;
- if (_callbackFnPtr)
+ if (_callbackFnPtr != nullptr)
(this->*_callbackFnPtr)();
}
+void RSound4::stopChannel(byte channel) {
+ getChannel(channel)->_pSrc = loadData(0x1188);
+}
+
int RSound4::command9() {
command1();
setFadeOutSpeed((byte)_commandParam);
return 0;
}
-byte RSound4::paramToVariant() {
- return (byte)((_commandParam >> 1) + 36);
+void RSound4::playCommand10_58CommonChannels() {
+ playSoundStatic(0x1274, 1);
+ playSoundStatic(0x13A6, 2);
+ playSoundStatic(0x15E4, 3);
+}
+
+int RSound4::command10() {
+ command1();
+ playSoundStatic(0x17DE, 4);
+ playSoundStatic(0x181C, 9);
+ playCommand10_58CommonChannels();
+ return 0;
}
void RSound4::setCommand12Variant() {
- byte value = paramToVariant();
+ byte value = (byte)((_commandParam >> 1) + 36);
loadData(0x1966)[1] = value;
loadData(0x1DC8)[1] = value;
loadData(0x1FBA)[1] = value;
@@ -1367,98 +1343,84 @@ void RSound4::setCommand12Variant() {
}
int RSound4::command12() {
- if (_channels[4]._soundData == loadData(0x1966) && _channels[4]._deltaCounter) {
+ if (isSoundPlaying(5, loadData(0x1966))) {
setCommand12Variant();
return 0;
}
setCommand12Variant();
command1();
- _channels[4].playData(loadData(0x1966));
- _channels[5].playData(loadData(0x1DC8));
- _channels[6].playData(loadData(0x1FBA));
- _channels[7].playData(loadData(0x211E));
- _channels[8].playData(loadData(0x24A8));
- return 0;
-}
-
-void RSound4::loadIntroChannels() {
- _channels[0].playData(loadData(0x1274));
- _channels[1].playData(loadData(0x13A6));
- _channels[2].playData(loadData(0x15E4));
-}
-
-int RSound4::command10() {
- command1();
- _channels[3].playData(loadData(0x17DE));
- _channels[8].playData(loadData(0x181C));
- loadIntroChannels();
+ playSoundStatic(0x1966, 5);
+ playSoundStatic(0x1DC8, 6);
+ playSoundStatic(0x1FBA, 7);
+ playSoundStatic(0x211E, 8);
+ playSoundStatic(0x24A8, 9);
return 0;
}
int RSound4::command19() {
- playSoundCh5To8(0x1196);
+ playSoundDynamic(0x1196);
return 0;
}
int RSound4::command20() {
- playSoundCh5To8(0x118A);
+ playSoundDynamic(0x118A);
return 0;
}
int RSound4::command21() {
- playSoundCh5To8(0x1260);
- playSoundCh5To8(0x126A);
+ playSoundDynamic(0x1260);
+ playSoundDynamic(0x126A);
return 0;
}
int RSound4::command27() {
- playSoundCh5To8(0x1220);
+ playSoundDynamic(0x1220);
return 0;
}
int RSound4::command30() {
- playSoundCh5To8(0x1216);
- playSoundCh5To8(0x120C);
+ playSoundDynamic(0x1216);
+ playSoundDynamic(0x120C);
return 0;
}
int RSound4::command32() {
- playSoundCh5To8(0x11D4);
+ playSoundDynamic(0x11D4);
return 0;
}
int RSound4::command33() {
- playSoundCh5To8(0x11E0);
+ playSoundDynamic(0x11E0);
return 0;
}
int RSound4::command34() {
- playSoundCh5To8(0x11EA);
+ playSoundDynamic(0x11EA);
return 0;
}
int RSound4::command35() {
- playSoundCh5To8(0x11FA);
+ playSoundDynamic(0x11FA);
return 0;
}
int RSound4::command36() {
- playSoundCh5To8(0x123C);
- playSoundCh5To8(0x1250);
+ playSoundDynamic(0x123C);
+ playSoundDynamic(0x1250);
return 0;
}
int RSound4::command37() {
- playSoundCh5To8(0x122E);
+ playSoundDynamic(0x122E);
return 0;
}
int RSound4::command52() {
- _channels[0]._pSrc = loadData(0x1188);
- _channels[1]._pSrc = loadData(0x1188);
- _channels[2]._pSrc = loadData(0x1188);
- _channels[4].playData(loadData(0x2A0C));
+ stopChannel(1);
+ stopChannel(2);
+ stopChannel(3);
+ playSoundStatic(0x2A0C, 5);
return 0;
}
@@ -1466,55 +1428,58 @@ int RSound4::command53() {
command1();
_callbackCounter = 56;
_callbackPeriod = 56;
- playSoundCh1To8(0x1888);
- playSoundCh1To8(0x18DE);
+ playSoundAnyChannel(0x1888);
+ playSoundAnyChannel(0x18DE);
return 0;
}
-void RSound4::loadCommand54() {
+void RSound4::command54Callback() {
_callbackFnPtr = nullptr;
- playSoundCh1To8(0x18B2);
- playSoundCh1To8(0x1904);
+ playSoundAnyChannel(0x18B2);
+ playSoundAnyChannel(0x1904);
}
int RSound4::command54() {
- _callbackFnPtr = &RSound4::loadCommand54;
+ // Add an instrument to command 53's music at the start of the next bar.
+ _callbackFnPtr = &RSound4::command54Callback;
return 0;
}
-void RSound4::loadCommand55() {
+void RSound4::command55Callback() {
_callbackFnPtr = nullptr;
- playSoundCh1To8(0x191E);
+ playSoundAnyChannel(0x191E);
}
int RSound4::command55() {
- _callbackFnPtr = &RSound4::loadCommand55;
+ // Add an instrument to command 53's music at the start of the next bar.
+ _callbackFnPtr = &RSound4::command55Callback;
return 0;
}
-void RSound4::loadCommand56() {
+void RSound4::command56Callback() {
_callbackFnPtr = nullptr;
- playSoundCh1To8(0x185C);
+ playSoundAnyChannel(0x185C);
}
int RSound4::command56() {
- _callbackFnPtr = &RSound4::loadCommand56;
+ // Add an instrument to command 53's music at the start of the next bar.
+ _callbackFnPtr = &RSound4::command56Callback;
return 0;
}
int RSound4::command57() {
- playSoundCh5To8(0x11C8);
+ playSoundDynamic(0x11C8);
return 0;
}
int RSound4::command58() {
- _channels[4]._pSrc = loadData(0x1188);
- loadIntroChannels();
+ stopChannel(5);
+ playCommand10_58CommonChannels();
return 0;
}
int RSound4::command59() {
- playSoundCh5To8(0x11BE);
+ playSoundDynamic(0x11BE);
return 0;
}
@@ -1523,11 +1488,11 @@ int RSound4::command59() {
const RSound5::CommandPtr RSound5::_commandList[42] = {
&RSound5::command0, &RSound5::command1, &RSound5::command2, &RSound5::command3,
&RSound5::command4, &RSound5::command5, &RSound5::command6, &RSound5::command7,
- &RSound5::command8, &RSound5::command9, &RSound5::command10, &RSound5::command11x24,
- &RSound5::command12x25, &RSound5::command13, &RSound5::command14, &RSound5::command15,
+ &RSound5::command8, &RSound5::command9, &RSound5::command10, &RSound5::command11_24,
+ &RSound5::command12_25, &RSound5::command13, &RSound5::command14, &RSound5::command15,
&RSound5::command16, &RSound5::command17, &RSound5::command18, &RSound5::command19,
&RSound5::command20, &RSound5::command21, &RSound5::command22, &RSound5::command23,
- &RSound5::command11x24, &RSound5::command12x25, &RSound5::command26, &RSound5::command27,
+ &RSound5::command11_24, &RSound5::command12_25, &RSound5::command26, &RSound5::command27,
&RSound5::command28, &RSound5::command29, &RSound5::command30, &RSound5::command31,
&RSound5::command32, &RSound5::command33, &RSound5::command34, &RSound5::command35,
&RSound5::command36, &RSound5::command37, &RSound5::command38, &RSound5::command39,
@@ -1547,190 +1512,195 @@ int RSound5::command(int commandId, int param) {
return (this->*_commandList[commandId])();
}
+void RSound5::stopChannel(byte channel) {
+ getChannel(channel)->_pSrc = loadData(0x1182);
+}
+
int RSound5::command9() {
- playSoundCh5To8(0x11C4);
+ playSoundDynamic(0x11C4);
return 0;
}
int RSound5::command10() {
- playSoundCh5To8(0x1238);
+ playSoundDynamic(0x1238);
return 0;
}
-int RSound5::command11x24() {
- playSoundCh5To8(0x1196);
+int RSound5::command11_24() {
+ playSoundDynamic(0x1196);
return 0;
}
-int RSound5::command12x25() {
- playSoundCh5To8(0x1242);
+int RSound5::command12_25() {
+ playSoundDynamic(0x1242);
return 0;
}
int RSound5::command13() {
- playSoundCh5To8(0x125E);
- playSoundCh5To8(0x1268);
+ playSoundDynamic(0x125E);
+ playSoundDynamic(0x1268);
return 0;
}
int RSound5::command14() {
- _channels[7].playData(loadData(0x1272));
+ playSoundStatic(0x1272, 8);
return 0;
}
int RSound5::command15() {
- if (_channels[7]._soundData == loadData(0x1272)) {
+ Channel *chan = getChannel(8);
+ if (chan->_soundData == loadData(0x1272)) {
byte *pData = loadData(0x1288);
- _channels[7]._innerLoopStart = pData;
- _channels[7]._outerLoopStart = pData;
- _channels[7]._deltaCounter = 1;
+ chan->_innerLoopStart = pData;
+ chan->_outerLoopStart = pData;
+ chan->_deltaCounter = 1;
}
return 0;
}
int RSound5::command16() {
- playSoundCh5To8(0x129A);
- playSoundCh5To8(0x129A);
- playSoundCh5To8(0x129A);
- playSoundCh5To8(0x129A);
+ playSoundDynamic(0x129A);
+ playSoundDynamic(0x129A);
+ playSoundDynamic(0x129A);
+ playSoundDynamic(0x129A);
return 0;
}
int RSound5::command17() {
- playSoundCh5To8(0x11B4);
+ playSoundDynamic(0x11B4);
return 0;
}
int RSound5::command18() {
- playSoundCh5To8(0x12E4);
- playSoundCh5To8(0x12F6);
- playSoundCh5To8(0x1308);
- playSoundCh5To8(0x131A);
+ playSoundDynamic(0x12E4);
+ playSoundDynamic(0x12F6);
+ playSoundDynamic(0x1308);
+ playSoundDynamic(0x131A);
return 0;
}
int RSound5::command19() {
- playSoundCh5To8(0x132C);
+ playSoundDynamic(0x132C);
return 0;
}
int RSound5::command20() {
- playSoundCh5To8(0x1368);
+ playSoundDynamic(0x1368);
return 0;
}
int RSound5::command21() {
- playSoundCh5To8(0x1388);
+ playSoundDynamic(0x1388);
return 0;
}
int RSound5::command22() {
- playSoundCh5To8(0x139A);
+ playSoundDynamic(0x139A);
return 0;
}
int RSound5::command23() {
- playSoundCh5To8(0x13AA);
- playSoundCh5To8(0x13AA);
- playSoundCh5To8(0x13AA);
- playSoundCh5To8(0x13AA);
+ playSoundDynamic(0x13AA);
+ playSoundDynamic(0x13AA);
+ playSoundDynamic(0x13AA);
+ playSoundDynamic(0x13AA);
return 0;
}
int RSound5::command26() {
- playSoundCh5To8(0x13D6);
+ playSoundDynamic(0x13D6);
return 0;
}
int RSound5::command27() {
- playSoundCh5To8(0x13F0);
+ playSoundDynamic(0x13F0);
return 0;
}
int RSound5::command28() {
- playSoundCh5To8(0x121C);
+ playSoundDynamic(0x121C);
return 0;
}
-void RSound5::loadTailChannels() {
- _channels[3].playData(loadData(0x1688));
- _channels[8].playData(loadData(0x1882));
+void RSound5::playCommand29_38CommonChannels() {
+ playSoundStatic(0x1688, 4);
+ playSoundStatic(0x1882, 9);
}
int RSound5::command29() {
byte *pData = loadData(0x1488);
if (!isSoundPlaying(pData)) {
command1();
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0x1534));
- _channels[2].playData(loadData(0x15EA));
- loadTailChannels();
+ playSoundStatic(pData, 1);
+ playSoundStatic(0x1534, 2);
+ playSoundStatic(0x15EA, 3);
+ playCommand29_38CommonChannels();
}
return 0;
}
int RSound5::command30() {
- playSoundCh5To8(0x1212);
- playSoundCh5To8(0x1208);
+ playSoundDynamic(0x1212);
+ playSoundDynamic(0x1208);
return 0;
}
int RSound5::command31() {
- playSoundCh5To8(0x140A);
+ playSoundDynamic(0x140A);
return 0;
}
int RSound5::command32() {
- playSoundCh5To8(0x11D0);
+ playSoundDynamic(0x11D0);
return 0;
}
int RSound5::command33() {
- playSoundCh5To8(0x11DC);
+ playSoundDynamic(0x11DC);
return 0;
}
int RSound5::command34() {
- playSoundCh5To8(0x11E6);
+ playSoundDynamic(0x11E6);
return 0;
}
int RSound5::command35() {
- playSoundCh5To8(0x11F6);
+ playSoundDynamic(0x11F6);
return 0;
}
int RSound5::command36() {
- playSoundCh5To8(0x1464);
- playSoundCh5To8(0x1478);
+ playSoundDynamic(0x1464);
+ playSoundDynamic(0x1478);
return 0;
}
int RSound5::command37() {
- playSoundCh5To8(0x122A);
+ playSoundDynamic(0x122A);
return 0;
}
int RSound5::command38() {
- _channels[4]._pSrc = loadData(0x1182);
- loadTailChannels();
+ stopChannel(5);
+ playCommand29_38CommonChannels();
return 0;
}
int RSound5::command39() {
- playSoundCh5To8(0x141E);
- playSoundCh5To8(0x1428);
+ playSoundDynamic(0x141E);
+ playSoundDynamic(0x1428);
return 0;
}
int RSound5::command40() {
- playSoundCh5To8(0x1432);
+ playSoundDynamic(0x1432);
return 0;
}
int RSound5::command41() {
- _channels[8]._pSrc = loadData(0x1182);
- _channels[3].playData(loadData(0x1BB6));
+ stopChannel(9);
+ playSoundStatic(0x1BB6, 4);
return 0;
}
@@ -1740,11 +1710,11 @@ const RSound6::CommandPtr RSound6::_commandList[30] = {
&RSound6::command0, &RSound6::command1, &RSound6::command2, &RSound6::command3,
&RSound6::command4, &RSound6::command5, &RSound6::command6, &RSound6::command7,
&RSound6::command8, &RSound6::command9, &RSound6::command10, &RSound6::command11,
- &RSound6::command12, &RSound6::command13x14, &RSound6::command13x14, &RSound6::command15,
+ &RSound6::command12, &RSound6::command13_14, &RSound6::command13_14, &RSound6::command15,
&RSound6::command16, &RSound6::command17, &RSound6::command18, &RSound6::command19,
&RSound6::command20, &RSound6::command21, &RSound6::command22, &RSound6::command23,
&RSound6::command24, &RSound6::command25, &RSound6::nullCommand, &RSound6::nullCommand,
- &RSound6::nullCommand, &RSound6::command28
+ &RSound6::nullCommand, &RSound6::command29
};
RSound6::RSound6(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
@@ -1761,148 +1731,150 @@ int RSound6::command(int commandId, int param) {
}
void RSound6::tickCallback() {
- if (!_callbackPeriod)
- return;
- if (--_callbackCounter)
+ if (_callbackPeriod == 0 || --_callbackCounter > 0)
return;
_callbackCounter = _callbackPeriod;
- if (_callbackFnPtr)
+ if (_callbackFnPtr != nullptr)
(this->*_callbackFnPtr)();
}
-void RSound6::reloadCommand24() {
- _callbackFnPtr = nullptr;
- command1();
- _callbackCounter = 84;
- _callbackPeriod = 84;
- _channels[0].playData(loadData(0x1A38));
- _channels[1].playData(loadData(0x1BBE));
- _channels[8].playData(loadData(0x1B90));
-}
-
-void RSound6::reloadCommand28() {
- _callbackFnPtr = nullptr;
- command1();
- _callbackCounter = 84;
- _callbackPeriod = 84;
- _channels[0].playData(loadData(0x130A));
- _channels[1].playData(loadData(0x13B6));
- _channels[2].playData(loadData(0x146C));
- _channels[3].playData(loadData(0x150A));
- _channels[8].playData(loadData(0x1704));
-}
-
int RSound6::command9() {
- playSoundCh5To8(0x111C);
+ playSoundDynamic(0x111C);
return 0;
}
int RSound6::command10() {
- playSoundCh5To8(0x1190);
+ playSoundDynamic(0x1190);
return 0;
}
int RSound6::command11() {
- playSoundCh5To8(0x11A4);
- playSoundCh5To8(0x11A4);
- playSoundCh5To8(0x11A4);
- playSoundCh5To8(0x11A4);
+ playSoundDynamic(0x11A4);
+ playSoundDynamic(0x11A4);
+ playSoundDynamic(0x11A4);
+ playSoundDynamic(0x11A4);
return 0;
}
int RSound6::command12() {
- playSoundCh5To8(0x11C8);
+ playSoundDynamic(0x11C8);
return 0;
}
-int RSound6::command13x14() {
- playSoundCh5To8(0x11E8);
+int RSound6::command13_14() {
+ playSoundDynamic(0x11E8);
return 0;
}
int RSound6::command15() {
- playSoundCh5To8(0x11F2);
- playSoundCh5To8(0x11F2);
- playSoundCh5To8(0x11F2);
- playSoundCh5To8(0x11F2);
+ playSoundDynamic(0x11F2);
+ playSoundDynamic(0x11F2);
+ playSoundDynamic(0x11F2);
+ playSoundDynamic(0x11F2);
return 0;
}
int RSound6::command16() {
- playSoundCh5To8(0x121E);
+ playSoundDynamic(0x121E);
return 0;
}
int RSound6::command17() {
- playSoundCh5To8(0x1228);
- playSoundCh5To8(0x1228);
- playSoundCh5To8(0x1228);
- playSoundCh5To8(0x1228);
+ playSoundDynamic(0x1228);
+ playSoundDynamic(0x1228);
+ playSoundDynamic(0x1228);
+ playSoundDynamic(0x1228);
return 0;
}
int RSound6::command18() {
- playSoundCh5To8(0x1254);
+ playSoundDynamic(0x1254);
return 0;
}
int RSound6::command19() {
- playSoundCh5To8(0x1266);
+ playSoundDynamic(0x1266);
return 0;
}
int RSound6::command20() {
- playSoundCh5To8(0x1278);
+ playSoundDynamic(0x1278);
return 0;
}
int RSound6::command21() {
- _channels[4].playData(loadData(0x1282));
- playSoundCh5To8(0x1282);
- playSoundCh5To8(0x1282);
- playSoundCh5To8(0x12AA);
+ playSoundStatic(0x1282, 5);
+ playSoundDynamic(0x1282);
+ playSoundDynamic(0x1282);
+ playSoundDynamic(0x12AA);
return 0;
}
int RSound6::command22() {
- _channels[4].playData(loadData(0x12CE));
- _channels[5].playData(loadData(0x12CE));
- _channels[6].playData(loadData(0x12CE));
- _channels[7].playData(loadData(0x12CE));
+ playSoundStatic(0x12CE, 5);
+ playSoundStatic(0x12CE, 6);
+ playSoundStatic(0x12CE, 7);
+ playSoundStatic(0x12CE, 8);
return 0;
}
int RSound6::command23() {
- playSoundCh5To8(0x1174);
+ playSoundDynamic(0x1174);
return 0;
}
+void RSound6::command24Callback() {
+ _callbackFnPtr = nullptr;
+ command1();
+ _callbackCounter = 84;
+ _callbackPeriod = 84;
+ playSoundStatic(0x1A38, 1);
+ playSoundStatic(0x1BBE, 2);
+ playSoundStatic(0x1B90, 9);
+}
+
int RSound6::command24() {
- if (_channels[0]._deltaCounter && _channels[0]._soundData == loadData(0x130A)) {
- _callbackFnPtr = &RSound6::reloadCommand24;
+ if (isSoundPlaying(1, loadData(0x130A))) {
+ // The music of command 29 is playing. Cut into command 24's music
+ // at the next bar, which is when the callback is invoked.
+ _callbackFnPtr = &RSound6::command24Callback;
return 0;
}
- reloadCommand24();
+ command24Callback();
return 0;
}
int RSound6::command25() {
- _channels[4].playData(loadData(0x12FE));
+ playSoundStatic(0x12FE, 5);
return 0;
}
-int RSound6::command28() {
+void RSound6::command29Callback() {
+ _callbackFnPtr = nullptr;
+ command1();
+ _callbackCounter = 84;
+ _callbackPeriod = 84;
+ playSoundStatic(0x130A, 1);
+ playSoundStatic(0x13B6, 2);
+ playSoundStatic(0x146C, 3);
+ playSoundStatic(0x150A, 4);
+ playSoundStatic(0x1704, 9);
+}
+
+int RSound6::command29() {
if (isSoundPlaying(loadData(0x130A)))
return 0;
- if (_channels[0]._deltaCounter && _channels[0]._soundData == loadData(0x1A38)) {
- _callbackFnPtr = &RSound6::reloadCommand28;
+ if (isSoundPlaying(1, loadData(0x1A38))) {
+ // The music of command 24 is playing. Cut into command 29's music
+ // at the next bar, which is when the callback is invoked.
+ _callbackFnPtr = &RSound6::command29Callback;
return 0;
}
- reloadCommand28();
+ command29Callback();
return 0;
}
@@ -1936,125 +1908,126 @@ int RSound7::command(int commandId, int param) {
int RSound7::command9() {
command1();
- _channels[0].playData(loadData(0x1C0E));
- _channels[1].playData(loadData(0x1C88));
- _channels[2].playData(loadData(0x1CD4));
- _channels[3].playData(loadData(0x1D4E));
+ playSoundStatic(0x1C0E, 1);
+ playSoundStatic(0x1C88, 2);
+ playSoundStatic(0x1CD4, 3);
+ playSoundStatic(0x1D4E, 4);
return 0;
}
int RSound7::command15() {
- playSoundCh5To8(0x125C);
+ playSoundDynamic(0x125C);
return 0;
}
int RSound7::command16() {
- playSoundCh5To8(0x12DE);
+ playSoundDynamic(0x12DE);
return 0;
}
int RSound7::command17() {
- playSoundCh5To8(0x12C2);
+ playSoundDynamic(0x12C2);
return 0;
}
int RSound7::command18() {
- _channels[7].playData(loadData(0x12FC));
+ playSoundStatic(0x12FC, 8);
return 0;
}
int RSound7::command19() {
- if (_channels[7]._soundData == loadData(0x12FC)) {
+ Channel *chan = getChannel(8);
+ if (chan->_soundData == loadData(0x12FC)) {
byte *pData = loadData(0x1312);
- _channels[7]._innerLoopStart = pData;
- _channels[7]._outerLoopStart = pData;
- _channels[7]._deltaCounter = 1;
+ chan->_innerLoopStart = pData;
+ chan->_outerLoopStart = pData;
+ chan->_deltaCounter = 1;
}
return 0;
}
int RSound7::command20() {
- playSoundCh5To8(0x1324);
- playSoundCh5To8(0x1324);
+ playSoundDynamic(0x1324);
+ playSoundDynamic(0x1324);
return 0;
}
int RSound7::command21() {
- playSoundCh5To8(0x1336);
+ playSoundDynamic(0x1336);
return 0;
}
int RSound7::command22() {
- playSoundCh5To8(0x1340);
+ playSoundDynamic(0x1340);
return 0;
}
int RSound7::command23() {
- playSoundCh5To8(0x12B4);
+ playSoundDynamic(0x12B4);
return 0;
}
int RSound7::command24() {
- _channels[0].playData(loadData(0x137C));
- _channels[1].playData(loadData(0x1406));
- _channels[2].playData(loadData(0x1492));
- _channels[3].playData(loadData(0x1516));
- _channels[4].playData(loadData(0x1588));
+ playSoundStatic(0x137C, 1);
+ playSoundStatic(0x1406, 2);
+ playSoundStatic(0x1492, 3);
+ playSoundStatic(0x1516, 4);
+ playSoundStatic(0x1588, 5);
return 0;
}
int RSound7::command25() {
command1();
- _channels[0].playData(loadData(0x1612));
- _channels[1].playData(loadData(0x16C8));
- _channels[2].playData(loadData(0x177E));
- _channels[3].playData(loadData(0x1838));
+ playSoundStatic(0x1612, 1);
+ playSoundStatic(0x16C8, 2);
+ playSoundStatic(0x177E, 3);
+ playSoundStatic(0x1838, 4);
return 0;
}
int RSound7::command27() {
- _channels[0].playData(loadData(0x1932));
- _channels[1].playData(loadData(0x1986));
- _channels[2].playData(loadData(0x19EC));
- _channels[3].playData(loadData(0x1A66));
- _channels[4].playData(loadData(0x1B3C));
+ playSoundStatic(0x1932, 1);
+ playSoundStatic(0x1986, 2);
+ playSoundStatic(0x19EC, 3);
+ playSoundStatic(0x1A66, 4);
+ playSoundStatic(0x1B3C, 5);
return 0;
}
int RSound7::command30() {
- playSoundCh5To8(0x12AA);
- playSoundCh5To8(0x12A0);
+ playSoundDynamic(0x12AA);
+ playSoundDynamic(0x12A0);
return 0;
}
int RSound7::command32() {
- playSoundCh5To8(0x1268);
+ playSoundDynamic(0x1268);
return 0;
}
int RSound7::command33() {
- playSoundCh5To8(0x1274);
+ playSoundDynamic(0x1274);
return 0;
}
int RSound7::command34() {
- playSoundCh5To8(0x127E);
+ playSoundDynamic(0x127E);
return 0;
}
int RSound7::command35() {
- playSoundCh5To8(0x128E);
+ playSoundDynamic(0x128E);
return 0;
}
int RSound7::command36() {
- playSoundCh5To8(0x1358);
- playSoundCh5To8(0x136C);
+ playSoundDynamic(0x1358);
+ playSoundDynamic(0x136C);
return 0;
}
int RSound7::command37() {
- playSoundCh5To8(0x134A);
+ playSoundDynamic(0x134A);
return 0;
}
@@ -2087,118 +2060,118 @@ int RSound8::command(int commandId, int param) {
}
int RSound8::command9() {
- playSoundCh5To8(0x10BA);
+ playSoundDynamic(0x10BA);
return 0;
}
int RSound8::command10() {
- _channels[0].playData(loadData(0x115A));
- _channels[1].playData(loadData(0x115A));
- _channels[2].playData(loadData(0x115A));
- _channels[3].playData(loadData(0x1150));
+ playSoundStatic(0x115A, 1);
+ playSoundStatic(0x115A, 2);
+ playSoundStatic(0x115A, 3);
+ playSoundStatic(0x1150, 4);
return 0;
}
int RSound8::command11() {
- playSoundCh5To8(0x1194);
+ playSoundDynamic(0x1194);
return 0;
}
int RSound8::command12() {
- playSoundCh5To8(0x11B2);
+ playSoundDynamic(0x11B2);
return 0;
}
int RSound8::command13() {
- playSoundCh5To8(0x11D0);
- playSoundCh5To8(0x11D0);
- playSoundCh5To8(0x11D0);
- playSoundCh5To8(0x11D0);
+ playSoundDynamic(0x11D0);
+ playSoundDynamic(0x11D0);
+ playSoundDynamic(0x11D0);
+ playSoundDynamic(0x11D0);
return 0;
}
-void RSound8::setCommand1415Variant(byte v1, byte v2) {
+void RSound8::playCommand14_15Variant(byte v1, byte v2) {
byte *pData = loadData(0x1204);
pData[3] = v1;
pData[6] = v2;
pData[9] = v2;
- playSoundCh5To8(0x1204);
- playSoundCh5To8(0x1204);
- playSoundCh5To8(0x1204);
- playSoundCh5To8(0x1204);
+ playSoundDynamic(0x1204);
+ playSoundDynamic(0x1204);
+ playSoundDynamic(0x1204);
+ playSoundDynamic(0x1204);
}
int RSound8::command14() {
- setCommand1415Variant(40, 1);
+ playCommand14_15Variant(40, 1);
return 0;
}
int RSound8::command15() {
- setCommand1415Variant(100, 255);
+ playCommand14_15Variant(100, 255);
return 0;
}
int RSound8::command16() {
- playSoundCh5To8(0x112E);
- playSoundCh5To8(0x112E);
+ playSoundDynamic(0x112E);
+ playSoundDynamic(0x112E);
return 0;
}
int RSound8::command17() {
- playSoundCh5To8(0x1234);
+ playSoundDynamic(0x1234);
return 0;
}
int RSound8::command18() {
- playSoundCh5To8(0x1244);
+ playSoundDynamic(0x1244);
return 0;
}
int RSound8::command19() {
- playSoundCh5To8(0x1254);
+ playSoundDynamic(0x1254);
return 0;
}
int RSound8::command20() {
- playSoundCh5To8(0x125E);
+ playSoundDynamic(0x125E);
return 0;
}
int RSound8::command21() {
- playSoundCh5To8(0x126E);
+ playSoundDynamic(0x126E);
return 0;
}
int RSound8::command22() {
- playSoundCh5To8(0x1278);
+ playSoundDynamic(0x1278);
return 0;
}
int RSound8::command23() {
- _channels[0].playData(loadData(0x128E));
- _channels[1].playData(loadData(0x128E));
- _channels[2].playData(loadData(0x128E));
- _channels[3].playData(loadData(0x128E));
+ playSoundStatic(0x128E, 1);
+ playSoundStatic(0x128E, 2);
+ playSoundStatic(0x128E, 3);
+ playSoundStatic(0x128E, 4);
return 0;
}
int RSound8::command24() {
- playSoundCh5To8(0x12B4);
+ playSoundDynamic(0x12B4);
return 0;
}
int RSound8::command25() {
- playSoundCh5To8(0x12CA);
+ playSoundDynamic(0x12CA);
return 0;
}
int RSound8::command26() {
- playSoundCh5To8(0x12DC);
+ playSoundDynamic(0x12DC);
return 0;
}
int RSound8::command27() {
- playSoundCh5To8(0x1112);
+ playSoundDynamic(0x1112);
return 0;
}
@@ -2206,9 +2179,9 @@ int RSound8::command28() {
byte *pData = loadData(0x130A);
if (!isSoundPlaying(pData)) {
command1();
- _channels[0].playData(pData);
- _channels[1].playData(loadData(0x1480));
- _channels[8].playData(loadData(0x154C));
+ playSoundStatic(pData, 1);
+ playSoundStatic(0x1480, 2);
+ playSoundStatic(0x154C, 9);
}
return 0;
}
@@ -2217,51 +2190,51 @@ int RSound8::command29() {
byte *pData = loadData(0x15FC);
if (!isSoundPlaying(pData)) {
command1();
- _channels[2].playData(pData);
- _channels[8].playData(loadData(0x1652));
+ playSoundStatic(pData, 3);
+ playSoundStatic(0x1652, 9);
}
return 0;
}
int RSound8::command30() {
- playSoundCh5To8(0x1108);
- playSoundCh5To8(0x10FE);
+ playSoundDynamic(0x1108);
+ playSoundDynamic(0x10FE);
return 0;
}
int RSound8::command31() {
- playSoundCh5To8(0x1140);
+ playSoundDynamic(0x1140);
return 0;
}
int RSound8::command32() {
- playSoundCh5To8(0x10C6);
+ playSoundDynamic(0x10C6);
return 0;
}
int RSound8::command33() {
- playSoundCh5To8(0x10D2);
+ playSoundDynamic(0x10D2);
return 0;
}
int RSound8::command34() {
- playSoundCh5To8(0x10DC);
+ playSoundDynamic(0x10DC);
return 0;
}
int RSound8::command35() {
- playSoundCh5To8(0x10EC);
+ playSoundDynamic(0x10EC);
return 0;
}
int RSound8::command36() {
- playSoundCh5To8(0x12E6);
- playSoundCh5To8(0x12FA);
+ playSoundDynamic(0x12E6);
+ playSoundDynamic(0x12FA);
return 0;
}
int RSound8::command37() {
- playSoundCh5To8(0x1120);
+ playSoundDynamic(0x1120);
return 0;
}
@@ -2300,13 +2273,11 @@ int RSound9::command(int commandId, int param) {
}
void RSound9::tickCallback() {
- if (!_callbackPeriod)
- return;
- if (--_callbackCounter)
+ if (_callbackPeriod == 0 || --_callbackCounter > 0)
return;
_callbackCounter = _callbackPeriod;
- if (_callbackFnPtr)
+ if (_callbackFnPtr != nullptr)
(this->*_callbackFnPtr)();
}
@@ -2320,66 +2291,66 @@ int RSound9::command0() {
int RSound9::command9() {
_callbackCounter = 1848;
_callbackPeriod = 84;
- _channels[0].playData(loadData(0x16E4));
- _channels[1].playData(loadData(0x1E9E));
- _channels[2].playData(loadData(0x2F9C));
- _channels[3].playData(loadData(0x2644));
- _channels[4].playData(loadData(0x1FF4));
- _channels[5].playData(loadData(0x2382));
- _channels[6].playData(loadData(0x1AAE));
+ playSoundStatic(0x16E4, 1);
+ playSoundStatic(0x1E9E, 2);
+ playSoundStatic(0x2F9C, 3);
+ playSoundStatic(0x2644, 4);
+ playSoundStatic(0x1FF4, 5);
+ playSoundStatic(0x2382, 6);
+ playSoundStatic(0x1AAE, 7);
return 0;
}
int RSound9::command10() {
- _channels[0].playData(loadData(0x31E2));
- _channels[1].playData(loadData(0x31FA));
- _channels[2].playData(loadData(0x3212));
- _channels[3].playData(loadData(0x322C));
+ playSoundStatic(0x31E2, 1);
+ playSoundStatic(0x31FA, 2);
+ playSoundStatic(0x3212, 3);
+ playSoundStatic(0x322C, 4);
return 0;
}
int RSound9::command11() {
- _channels[7].playData(loadData(0x33E2));
+ playSoundStatic(0x33E2, 8);
return 0;
}
int RSound9::command12() {
- _channels[7].playData(loadData(0x342E));
+ playSoundStatic(0x342E, 8);
return 0;
}
int RSound9::command13() {
- _channels[7].playData(loadData(0x343A));
+ playSoundStatic(0x343A, 8);
return 0;
}
int RSound9::command14() {
- _channels[7].playData(loadData(0x3442));
+ playSoundStatic(0x3442, 8);
return 0;
}
int RSound9::command15() {
- _channels[7].playData(loadData(0x3462));
+ playSoundStatic(0x3462, 8);
return 0;
}
int RSound9::command16() {
- _channels[7].playData(loadData(0x347A));
+ playSoundStatic(0x347A, 8);
return 0;
}
int RSound9::command17() {
- _channels[7].playData(loadData(0x3470));
+ playSoundStatic(0x3470, 8);
return 0;
}
int RSound9::command18() {
- playSoundCh5To8(0x3248);
+ playSoundDynamic(0x3248);
return 0;
}
int RSound9::command19() {
- playSoundCh5To8(0x3262);
+ playSoundDynamic(0x3262);
return 0;
}
@@ -2387,7 +2358,7 @@ int RSound9::command20() {
int v = (generateRandomNumber() & 24) + 77;
byte *pData = loadData(0x3284);
pData[6] = v & 0x7F;
- playSoundCh5To8(0x3284);
+ playSoundDynamic(0x3284);
return 0;
}
@@ -2395,7 +2366,7 @@ int RSound9::command21() {
byte *pData = loadData(0x3298);
pData[9] = 70;
if (!isSoundPlaying(pData))
- playSoundCh5To8(0x3298);
+ playSoundDynamic(0x3298);
return 0;
}
@@ -2403,42 +2374,42 @@ int RSound9::command22() {
byte *pData = loadData(0x3298);
pData[9] = 45;
if (!isSoundPlaying(pData))
- playSoundCh5To8(0x3298);
+ playSoundDynamic(0x3298);
return 0;
}
int RSound9::command23() {
- Channel *chan = playSoundCh5To8(0x32B0);
- if (chan)
+ Channel *chan = playSoundDynamic(0x32B0);
+ if (chan != nullptr)
chan->_innerLoopStart = loadData(0x32CE);
- chan = playSoundCh5To8(0x32B6);
- if (chan)
+ chan = playSoundDynamic(0x32B6);
+ if (chan != nullptr)
chan->_innerLoopStart = loadData(0x32CE);
- chan = playSoundCh5To8(0x32C8);
- if (chan)
+ chan = playSoundDynamic(0x32C8);
+ if (chan != nullptr)
chan->_innerLoopStart = loadData(0x32CE);
return 0;
}
int RSound9::command24() {
- playSoundCh5To8(0x32E0);
+ playSoundDynamic(0x32E0);
return 0;
}
int RSound9::command25() {
- playSoundCh5To8(0x32F6);
+ playSoundDynamic(0x32F6);
return 0;
}
int RSound9::command26() {
- playSoundCh5To8(0x331A);
+ playSoundDynamic(0x331A);
return 0;
}
int RSound9::command27() {
- playSoundCh5To8(0x3332);
+ playSoundDynamic(0x3332);
return 0;
}
@@ -2446,7 +2417,7 @@ int RSound9::command28() {
int v = (generateRandomNumber() & 28) + 15;
byte *pData = loadData(0x334A);
pData[6] = v & 0x7F;
- _channels[7].playData(pData);
+ playSoundStatic(pData, 8);
return 0;
}
@@ -2454,29 +2425,29 @@ int RSound9::command29() {
int v = (generateRandomNumber() & 12) + 33;
byte *pData = loadData(0x335E);
pData[6] = v & 0x7F;
- playSoundCh5To8(0x335E);
+ playSoundDynamic(0x335E);
return 0;
}
int RSound9::command30() {
- playSoundCh5To8(0x3386);
+ playSoundDynamic(0x3386);
return 0;
}
int RSound9::command31() {
- playSoundCh5To8(0x3396);
- playSoundCh5To8(0x33A4);
- playSoundCh5To8(0x33B2);
+ playSoundDynamic(0x3396);
+ playSoundDynamic(0x33A4);
+ playSoundDynamic(0x33B2);
return 0;
}
int RSound9::command32() {
- playSoundCh5To8(0x33C0);
+ playSoundDynamic(0x33C0);
return 0;
}
int RSound9::command33() {
- playSoundCh5To8(0x33CA);
+ playSoundDynamic(0x33CA);
return 0;
}
@@ -2489,28 +2460,28 @@ int RSound9::command34() {
*loadData(0x7DCD) = 2;
*loadData(0x8791) = 2;
- _channels[0].playData(loadData(0x4D2A));
- _channels[1].playData(loadData(0x51AA));
- _channels[2].playData(loadData(0x5634));
- _channels[3].playData(loadData(0x6844));
- _channels[4].playData(loadData(0x7DD0));
+ playSoundStatic(0x4D2A, 1);
+ playSoundStatic(0x51AA, 2);
+ playSoundStatic(0x5634, 3);
+ playSoundStatic(0x6844, 4);
+ playSoundStatic(0x7DD0, 5);
return 0;
}
int RSound9::command35() {
- playSoundCh5To8(0x344C);
+ playSoundDynamic(0x344C);
return 0;
}
int RSound9::command36() {
- playSoundCh5To8(0x334A);
+ playSoundDynamic(0x334A);
- Channel *chan = playSoundCh5To8(0x32C2);
- if (chan)
+ Channel *chan = playSoundDynamic(0x32C2);
+ if (chan != nullptr)
chan->_innerLoopStart = loadData(0x3378);
- chan = playSoundCh5To8(0x32BC);
- if (chan)
+ chan = playSoundDynamic(0x32BC);
+ if (chan != nullptr)
chan->_innerLoopStart = loadData(0x3368);
return 0;
}
@@ -2519,169 +2490,169 @@ int RSound9::command37() {
int v = (generateRandomNumber() & 2) + 72;
byte *pData = loadData(0x349C);
pData[6] = v & 0x7F;
- playSoundCh5To8(0x349C);
+ playSoundDynamic(0x349C);
return 0;
}
int RSound9::command38() {
- _callbackFnPtr = &RSound9::loadCommand38;
+ _callbackFnPtr = &RSound9::command38Callback;
return 0;
}
-void RSound9::loadCommand38() {
+void RSound9::command38Callback() {
_callbackFnPtr = nullptr;
- _channels[0].playData(loadData(0x1878));
- _channels[1].playData(loadData(0x1F64));
- _channels[2].playData(loadData(0x308E));
- _channels[3].playData(loadData(0x2A10));
- _channels[4].playData(loadData(0x21C8));
- _channels[5].playData(loadData(0x2558));
- _channels[6].playData(loadData(0x1E9A));
+ playSoundStatic(0x1878, 1);
+ playSoundStatic(0x1F64, 2);
+ playSoundStatic(0x308E, 3);
+ playSoundStatic(0x2A10, 4);
+ playSoundStatic(0x21C8, 5);
+ playSoundStatic(0x2558, 6);
+ playSoundStatic(0x1E9A, 7);
}
int RSound9::command39() {
- _callbackFnPtr = &RSound9::loadCommand39;
+ _callbackFnPtr = &RSound9::command39Callback;
return 0;
}
-void RSound9::loadCommand39() {
+void RSound9::command39Callback() {
_callbackFnPtr = nullptr;
- _channels[0].playData(loadData(0x1A24));
- _channels[1].playData(loadData(0x1FD0));
- _channels[2].playData(loadData(0x318A));
- _channels[3].playData(loadData(0x2E4A));
- _channels[4].playData(loadData(0x2380));
- _channels[5].playData(loadData(0x2642));
- _channels[6].playData(loadData(0x1E9C));
+ playSoundStatic(0x1A24, 1);
+ playSoundStatic(0x1FD0, 2);
+ playSoundStatic(0x318A, 3);
+ playSoundStatic(0x2E4A, 4);
+ playSoundStatic(0x2380, 5);
+ playSoundStatic(0x2642, 6);
+ playSoundStatic(0x1E9C, 7);
}
int RSound9::command40() {
- _callbackFnPtr = &RSound9::loadCommand40;
+ _callbackFnPtr = &RSound9::command40Callback;
return 0;
}
-void RSound9::loadCommand40() {
+void RSound9::command40Callback() {
_callbackFnPtr = nullptr;
- _channels[0].playData(loadData(0x4F00));
- _channels[1].playData(loadData(0x534A));
- _channels[2].playData(loadData(0x5CDE));
- _channels[3].playData(loadData(0x6F8E));
- _channels[4].playData(loadData(0x8110));
+ playSoundStatic(0x4F00, 1);
+ playSoundStatic(0x534A, 2);
+ playSoundStatic(0x5CDE, 3);
+ playSoundStatic(0x6F8E, 4);
+ playSoundStatic(0x8110, 5);
}
int RSound9::command41() {
- _callbackFnPtr = &RSound9::loadCommand41;
+ _callbackFnPtr = &RSound9::command41Callback;
return 0;
}
-void RSound9::loadCommand41() {
+void RSound9::command41Callback() {
_callbackFnPtr = nullptr;
- _channels[0].playData(loadData(0x4F26));
- _channels[1].playData(loadData(0x53BC));
- _channels[2].playData(loadData(0x5DFE));
- _channels[3].playData(loadData(0x747E));
- _channels[4].playData(loadData(0x8340));
+ playSoundStatic(0x4F26, 1);
+ playSoundStatic(0x53BC, 2);
+ playSoundStatic(0x5DFE, 3);
+ playSoundStatic(0x747E, 4);
+ playSoundStatic(0x8340, 5);
}
int RSound9::command42() {
- _callbackFnPtr = &RSound9::loadCommand42;
+ _callbackFnPtr = &RSound9::command42Callback;
return 0;
}
-void RSound9::loadCommand42() {
+void RSound9::command42Callback() {
_callbackFnPtr = nullptr;
- _channels[0].playData(loadData(0x4F30));
- _channels[1].playData(loadData(0x555C));
- _channels[2].playData(loadData(0x6582));
- _channels[3].playData(loadData(0x7A2E));
- _channels[4].playData(loadData(0x8480));
+ playSoundStatic(0x4F30, 1);
+ playSoundStatic(0x555C, 2);
+ playSoundStatic(0x6582, 3);
+ playSoundStatic(0x7A2E, 4);
+ playSoundStatic(0x8480, 5);
}
int RSound9::command43() {
_callbackCounter = 80;
_callbackPeriod = 80;
- _channels[0].playData(loadData(0x34BE));
- _channels[1].playData(loadData(0x3A46));
- _channels[2].playData(loadData(0x3F52));
- _channels[3].playData(loadData(0x439A));
+ playSoundStatic(0x34BE, 1);
+ playSoundStatic(0x3A46, 2);
+ playSoundStatic(0x3F52, 3);
+ playSoundStatic(0x439A, 4);
return 0;
}
int RSound9::command44_46() {
- _callbackFnPtr = &RSound9::loadCommand44_46;
+ _callbackFnPtr = &RSound9::command44_46Callback;
return 0;
}
-void RSound9::loadCommand44_46() {
+void RSound9::command44_46Callback() {
_callbackFnPtr = nullptr;
- _channels[0].playData(loadData(0x3518));
- _channels[1].playData(loadData(0x3AA2));
- _channels[2].playData(loadData(0x403A));
- _channels[3].playData(loadData(0x4486));
+ playSoundStatic(0x3518, 1);
+ playSoundStatic(0x3AA2, 2);
+ playSoundStatic(0x403A, 3);
+ playSoundStatic(0x4486, 4);
}
int RSound9::command45() {
- _callbackFnPtr = &RSound9::loadCommand45;
+ _callbackFnPtr = &RSound9::command45Callback;
return 0;
}
-void RSound9::loadCommand45() {
+void RSound9::command45Callback() {
_callbackFnPtr = nullptr;
- _channels[0].playData(loadData(0x37AC));
- _channels[1].playData(loadData(0x3CF8));
- _channels[2].playData(loadData(0x41E6));
- _channels[3].playData(loadData(0x4630));
+ playSoundStatic(0x37AC, 1);
+ playSoundStatic(0x3CF8, 2);
+ playSoundStatic(0x41E6, 3);
+ playSoundStatic(0x4630, 4);
}
int RSound9::command47() {
- _callbackFnPtr = &RSound9::loadCommand47;
+ _callbackFnPtr = &RSound9::command47Callback;
return 0;
}
-void RSound9::loadCommand47() {
+void RSound9::command47Callback() {
_callbackFnPtr = nullptr;
- _channels[0].playData(loadData(0x47D6));
- _channels[1].playData(loadData(0x48FA));
- _channels[2].playData(loadData(0x4A20));
- _channels[3].playData(loadData(0x4BAE));
+ playSoundStatic(0x47D6, 1);
+ playSoundStatic(0x48FA, 2);
+ playSoundStatic(0x4A20, 3);
+ playSoundStatic(0x4BAE, 4);
}
int RSound9::command48() {
byte *pData = loadData(0x34B0);
pData[6] ^= 0x1F;
pData[0xA] ^= 0x1F;
- playSoundCh5To8(0x34B0);
+ playSoundDynamic(0x34B0);
return 0;
}
int RSound9::command49() {
- _channels[0].playData(loadData(0x12CA));
- _channels[1].playData(loadData(0x132A));
- _channels[2].playData(loadData(0x1384));
- _channels[3].playData(loadData(0x1666));
- _channels[4].playData(loadData(0x1682));
- _channels[5].playData(loadData(0x16A0));
- _channels[6].playData(loadData(0x16BE));
+ playSoundStatic(0x12CA, 1);
+ playSoundStatic(0x132A, 2);
+ playSoundStatic(0x1384, 3);
+ playSoundStatic(0x1666, 4);
+ playSoundStatic(0x1682, 5);
+ playSoundStatic(0x16A0, 6);
+ playSoundStatic(0x16BE, 7);
return 0;
}
int RSound9::command50() {
- _callbackFnPtr = &RSound9::loadCommand50;
+ _callbackFnPtr = &RSound9::command50Callback;
return 0;
}
-void RSound9::loadCommand50() {
+void RSound9::command50Callback() {
_callbackFnPtr = nullptr;
*loadData(0x6841) = 0;
*loadData(0x7DCD) = 0;
*loadData(0x8791) = 0;
- _channels[0].playData(loadData(0x50B8));
- _channels[1].playData(loadData(0x7DCE));
- _channels[2].playData(loadData(0x676A));
- _channels[3].playData(loadData(0x7D08));
- _channels[4].playData(loadData(0x85C4));
+ playSoundStatic(0x50B8, 1);
+ playSoundStatic(0x7DCE, 2);
+ playSoundStatic(0x676A, 3);
+ playSoundStatic(0x7D08, 4);
+ playSoundStatic(0x85C4, 5);
}
int RSound9::command51() {
@@ -2693,11 +2664,11 @@ int RSound9::command51() {
*loadData(0x7DCD) = 2;
*loadData(0x8791) = 2;
- _channels[0].playData(loadData(0x4D3C));
- _channels[1].playData(loadData(0x51EE));
- _channels[2].playData(loadData(0x5A62));
- _channels[3].playData(loadData(0x6986));
- _channels[4].playData(loadData(0x7E5C));
+ playSoundStatic(0x4D3C, 1);
+ playSoundStatic(0x51EE, 2);
+ playSoundStatic(0x5A62, 3);
+ playSoundStatic(0x6986, 4);
+ playSoundStatic(0x7E5C, 5);
return 0;
}
diff --git a/engines/mads/nebular/sound/rsound_nebular.h b/engines/mads/nebular/sound/rsound_nebular.h
index c6ea4b15f20..63ffa6ea8ee 100644
--- a/engines/mads/nebular/sound/rsound_nebular.h
+++ b/engines/mads/nebular/sound/rsound_nebular.h
@@ -61,7 +61,7 @@ private:
* Shared loader for command11/12/13 - matches method1 in the
* disassembly (isSoundPlaying-gated command1() + 4-channel load).
*/
- void method1();
+ void playCommand11_12_13SharedChannels();
/**
* Clamp helper: param > 0x40 ? param - 0x40 : 0. Matches the same
@@ -115,7 +115,7 @@ private:
bool _command23Toggle;
byte adjustedCommandParam() const;
- void startCommand111213();
+ void playCommand11_12_13CommonChannels();
int executeDemoCommonCommand(int commandId);
public:
@@ -130,15 +130,13 @@ private:
/**
* Random-pick table used by command18 (TABLE1 in the disassembly).
- * Only entries 0-14 are ever reachable (command18 masks the random
- * index with decimal 30 = 0x1E), but all 16 confirmed clean entries
- * are kept for completeness. A further two rows of data follow
- * TABLE1 in seg001 but were never listed as reachable by any command
- * in this batch, and their values look like they may be text/other
- * data rather than further table entries - excluded until a command
- * turns up that actually reaches them.
+ * A further two rows of data follow TABLE1 in seg001 but were never
+ * listed as reachable by any command in this batch, and their values
+ * look like they may be text/other data rather than further table
+ * entries - excluded until a command turns up that actually reaches
+ * them.
*/
- static const uint16 _table1[16];
+ static const uint16 _command18RandomSfx[16];
/**
* Persistent counter (initial value
@@ -146,7 +144,7 @@ private:
* command12 runs; the low 7 bits are written into the sound data's
* pitch/note byte before playback. command5 resets it back to 47.
*/
- byte _pitchCycleCounter = 0x2F;
+ byte _volumeCycleCounter = 0x2F;
int command5();
@@ -208,15 +206,17 @@ private:
* Toggle used by command39/40 (initially 0). Shared between both commands: flips bit 2 (^= 4) on
* every call to either one, and the post-toggle value + 0x28 is
* written into the same sound data's byte 6, regardless of which of
- * the two commands triggered the toggle.
+ * the two commands triggered the toggle. This alternates the two
+ * command's played note value between 0x28 and 0x2C.
*/
- byte _command3940Toggle = 0;
+ byte _command39_40NoteToggle = 0;
/**
* Shared helper: pData[5] = value, then plays pData. Command 25
- * calls it for both native sequence offsets.
+ * calls it for both native sequence offsets. "method1" in
+ * disassembly.
*/
- Channel *method1(int offset, byte value);
+ Channel *patchAndPlaySound(int offset, byte value);
/**
* Matches the disassembly's OTHER "method1" (a same-named but
@@ -240,18 +240,6 @@ private:
*/
void sendDualVolume(byte volume);
- /**
- * Shared tail used by both command1
- * (falls through into it after calling command3()) and command5
- * (jumps straight into it after its isSoundPlaying gate). Enables
- * channels 5-8 (1-based; indices 4-7) - notably never reaches
- * channel 9.
- */
- void resetUpperChannelsTail();
-
- int command1();
- int command3();
- int command5();
int command9();
int command10();
int command11();
@@ -269,7 +257,7 @@ private:
int command24();
int command25();
int command26();
- int command27x42();
+ int command27_42();
int command28();
int command29();
int command30();
@@ -288,7 +276,7 @@ private:
int command44();
int command45();
int command46();
- int command47x49();
+ int command47_49();
int command48();
int command50();
int command51();
@@ -319,29 +307,29 @@ private:
typedef int (RSound4:: *CommandPtr)();
static const CommandPtr _commandList[60];
- /**
- * Called only from command12's shared
- * tail; computes (param >> 1) + 36.
- */
- byte paramToVariant();
-
/**
* Writes the same variant byte into
* offset 1 of the five sound blocks command12 (re)loads.
+ * Value is computed from command param.
*/
void setCommand12Variant();
/**
- * Shared tail of both command10 and
- * command58, loading channels 1-3 (1-based; indices 0-2).
+ * Shared tail of both command10 and command58, loading channels 1-3.
*/
- void loadIntroChannels();
+ void playCommand10_58CommonChannels();
void tickCallback() override;
- void loadCommand54();
- void loadCommand55();
- void loadCommand56();
+ void command54Callback();
+ void command55Callback();
+ void command56Callback();
+
+ /**
+ * Loads a null stream (containing event 00 00) into the specified
+ * channel, which stops playback on that channel.
+ */
+ void stopChannel(byte channel);
int command9();
int command10();
@@ -377,15 +365,20 @@ private:
static const CommandPtr _commandList[42];
/**
- * Shared tail of command29 and
- * command38, loading channels 4 and 9 (1-based; indices 3 and 8).
+ * Shared tail of command29 and command38, loading channels 4 and 9.
*/
- void loadTailChannels();
+ void playCommand29_38CommonChannels();
+
+ /**
+ * Loads a null stream (containing event 00 00) into the specified
+ * channel, which stops playback on that channel.
+ */
+ void stopChannel(byte channel);
int command9();
int command10();
- int command11x24();
- int command12x25();
+ int command11_24();
+ int command12_25();
int command13();
int command14();
int command15();
@@ -440,20 +433,20 @@ private:
void tickCallback() override;
/**
- * command24/command28's
+ * command24/command29's
* own full-reload bodies. When channel 1 is currently playing the
* OTHER command's theme, that command doesn't interrupt it
* immediately - it just points _callbackFnPtr at this same reload
* logic so the switch happens on the next callback tick instead.
*/
- void reloadCommand24();
- void reloadCommand28();
+ void command24Callback();
+ void command29Callback();
int command9();
int command10();
int command11();
int command12();
- int command13x14();
+ int command13_14();
int command15();
int command16();
int command17();
@@ -465,7 +458,7 @@ private:
int command23();
int command24();
int command25();
- int command28();
+ int command29();
public:
RSound6(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver);
@@ -512,7 +505,7 @@ private:
* Shared tail of command14/command15 -
* mutates three bytes of the shared sound data then plays it 4 times.
*/
- void setCommand1415Variant(byte v1, byte v2);
+ void playCommand14_15Variant(byte v1, byte v2);
int command9();
int command10();
@@ -613,16 +606,16 @@ private:
int command50();
int command51();
- // Deferred loader bodies, scheduled via _callbackFnPtr by the commands above
- void loadCommand38();
- void loadCommand39();
- void loadCommand40();
- void loadCommand41();
- void loadCommand42();
- void loadCommand44_46();
- void loadCommand45();
- void loadCommand47();
- void loadCommand50();
+ // Deferred callbacks, scheduled via _callbackFnPtr by the commands above
+ void command38Callback();
+ void command39Callback();
+ void command40Callback();
+ void command41Callback();
+ void command42Callback();
+ void command44_46Callback();
+ void command45Callback();
+ void command47Callback();
+ void command50Callback();
public:
RSound9(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver);
Commit: 75aee5d537a52045e9aec5470de66cd5185c06a4
https://github.com/scummvm/scummvm/commit/75aee5d537a52045e9aec5470de66cd5185c06a4
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-10-09T18:19:49+02:00
Commit Message:
MADS: Refactor Rex demo MT-32 code
Changed paths:
engines/mads/nebular/sound/rsound.cpp
engines/mads/nebular/sound/rsound.h
engines/mads/nebular/sound/rsound_nebular.cpp
engines/mads/nebular/sound/rsound_nebular.h
diff --git a/engines/mads/nebular/sound/rsound.cpp b/engines/mads/nebular/sound/rsound.cpp
index 3d01b5c5651..661992c348e 100644
--- a/engines/mads/nebular/sound/rsound.cpp
+++ b/engines/mads/nebular/sound/rsound.cpp
@@ -77,6 +77,17 @@ RSound::RSound(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver, const Common:
_fadeOutSpeed = (_fadeOutCheckMode == kRSoundFadeCheckAlternating ? 2 : 0);
_fadeOutCounter = _fadeOutSpeed;
+ _dynamicStartChannel = 5;
+ _dynamicIncludeChannel9 = false;
+ // The full game RSOUND drivers command 2 and 3 include channel 9 when
+ // initializing channel data and fading channels to stop, but command 2
+ // does not include channel 9 when initializing MIDI channels. Instead,
+ // command 4 includes channel 9 when initializing MIDI channels, but
+ // command 4 and 5 do not include channel 9 when initializing channel
+ // data and fading channels to stop. This is probably a bug, which is
+ // not replicated in this reimplementation.
+ _staticIncludeChannel9 = true;
+
for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i) {
_channels[i]._midiChannel = i + 1;
}
@@ -178,7 +189,7 @@ Channel *RSound::playSoundStatic(int offset, byte channel) {
}
Channel *RSound::playSoundDynamic(int offset) {
- return allocateAndPlay(loadData(offset));
+ return allocateAndPlay(loadData(offset), _dynamicStartChannel);
}
Channel *RSound::playSoundAnyChannel(int offset) {
@@ -214,6 +225,10 @@ Channel *RSound::allocateAndPlay(byte *pData, int startingChannel) {
return foundChannel;
}
+bool RSound::isSoundPlaying(int offset) {
+ return isSoundPlaying(loadData(offset));
+}
+
bool RSound::isSoundPlaying(byte *pData) {
// Deliberately excludes channel 9, matching the disassembly -
// same as allocateAndPlay()'s scan never reaching channel 9 either.
@@ -825,13 +840,19 @@ void RSound::resetAllChannels() {
* controllers, volume=100, pan=center) of MIDI channels [first, last]
* (both inclusive, 1-based). Shared tail used by command0/command2/command4.
*/
-void RSound::sendMidiChannelReset(int first, int last) {
+void RSound::sendMidiChannelReset(int first, int last, bool includeChannel9) {
for (int ch = first; ch <= last; ++ch) {
_midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | ch, MidiDriver::MIDI_CONTROLLER_ALL_NOTES_OFF, 0);
_midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | ch, MidiDriver::MIDI_CONTROLLER_RESET_ALL_CONTROLLERS, 0);
_midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | ch, MidiDriver::MIDI_CONTROLLER_VOLUME, 100);
_midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | ch, MidiDriver::MIDI_CONTROLLER_PANNING, 0x40);
}
+ if (last <= 8 && includeChannel9) {
+ _midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | 9, MidiDriver::MIDI_CONTROLLER_ALL_NOTES_OFF, 0);
+ _midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | 9, MidiDriver::MIDI_CONTROLLER_RESET_ALL_CONTROLLERS, 0);
+ _midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | 9, MidiDriver::MIDI_CONTROLLER_VOLUME, 100);
+ _midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | 9, MidiDriver::MIDI_CONTROLLER_PANNING, 0x40);
+ }
}
int RSound::command0() {
@@ -857,42 +878,39 @@ int RSound::command1() {
}
int RSound::command2() {
- // Channels 1-4 and 9 (also reinitializes the held-notes
+ // Initialize the static channels (also reinitializes the active notes
// table) plus the MIDI channel reset for those same channels.
- resetChannelRange(1, 4, true);
+ resetChannelRange(1, _dynamicStartChannel - 1, _staticIncludeChannel9);
clearActiveNotes();
setFadeOutSpeed(0);
- // The original code does not reset MIDI channel 9, which is
- // probably an oversight.
- sendMidiChannelReset(1, 4);
- sendMidiChannelReset(9, 9);
+ sendMidiChannelReset(1, _dynamicStartChannel - 1, _staticIncludeChannel9);
+ sendSysEx(_sysExOffset);
return 0;
}
int RSound::command3() {
- // Start fade-out to stop for channels 1-4 and 9.
+ // Start fade-out to stop for the static channels.
setFadeOutSpeed(1);
- for (int i = 1; i <= 4; ++i)
+ for (int i = 1; i < _dynamicStartChannel; ++i)
getChannel(i)->setFadeOut(true);
- getChannel(9)->setFadeOut(true);
+ if (_staticIncludeChannel9)
+ getChannel(9)->setFadeOut(true);
return 0;
}
int RSound::command4() {
- // Channels 5-8 (does NOT touch the held-notes
+ // Initialize the dynamic Channels (does NOT touch the active notes
// table) plus the MIDI channel reset for those same channels.
- resetChannelRange(5, 8);
+ resetChannelRange(_dynamicStartChannel, 8, _dynamicIncludeChannel9);
setFadeOutSpeed(0);
- // The original code also resets MIDI channel 9, which is
- // probably incorrect.
- sendMidiChannelReset(5, 8);
+ sendMidiChannelReset(_dynamicStartChannel, 8, _dynamicIncludeChannel9);
return 0;
}
int RSound::command5() {
- // Start fade-out to stop for channels 5-8.
+ // Start fade-out to stop for the dynamic channels.
setFadeOutSpeed(1);
- for (int i = 5; i <= 8; ++i)
+ for (int i = _dynamicStartChannel; i <= (_dynamicIncludeChannel9 ? 9 : 8); ++i)
getChannel(i)->setFadeOut(true);
return 0;
}
diff --git a/engines/mads/nebular/sound/rsound.h b/engines/mads/nebular/sound/rsound.h
index 64a17a2a848..998a3385272 100644
--- a/engines/mads/nebular/sound/rsound.h
+++ b/engines/mads/nebular/sound/rsound.h
@@ -216,6 +216,18 @@ private:
protected:
int _commandParam;
+ // Specifies the lowest channel in the channel range that is used for
+ // dynamic channel allocation. F.e. if this is 5, channels 1-4 are
+ // initialized and stopped by the static commands 2 and 3, and channels
+ // 5-8 are initialized and stopped by the dyanmic commands 4 and 5. Also,
+ // channels 5-8 are used by playSoundDynamic.
+ // Channel 9 can be included with the static or dynamic channels by setting
+ // the corresponding boolean field. This will only affect commands 2-5;
+ // channel 9 is never used by playSoundDynamic.
+ byte _dynamicStartChannel;
+ bool _staticIncludeChannel9;
+ bool _dynamicIncludeChannel9;
+
void setFadeOutSpeed(int fadeOutSpeed);
/**
@@ -296,12 +308,13 @@ protected:
* could be found, nullptr is returned and the sound data is not
* played.
*/
- Channel *allocateAndPlay(byte *pData, int startingChannel = 5);
+ Channel *allocateAndPlay(byte *pData, int startingChannel);
/**
* Checks to see whether the given block of data is already loaded
* into a channel and being played.
*/
+ bool isSoundPlaying(int offset);
bool isSoundPlaying(byte *pData);
/**
@@ -327,7 +340,7 @@ protected:
* controllers, volume=100, pan=center) of MIDI channels [first, last]
* (inclusive, 1-based). Shared tail used by command0/command2/command4.
*/
- void sendMidiChannelReset(int first, int last);
+ void sendMidiChannelReset(int first, int last, bool includeChannel9 = false);
/**
* Sets the volume for a channel. The value is set on the channel data
diff --git a/engines/mads/nebular/sound/rsound_nebular.cpp b/engines/mads/nebular/sound/rsound_nebular.cpp
index eb974dad174..7a8071f03a8 100644
--- a/engines/mads/nebular/sound/rsound_nebular.cpp
+++ b/engines/mads/nebular/sound/rsound_nebular.cpp
@@ -25,79 +25,6 @@ namespace MADS {
namespace RexNebular {
namespace Sound {
-RSoundDemo::RSoundDemo(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver, const Common::Path &filename,
- int dataOffset, int dataSize, int sysExOffset,
- int firstEffectChannel) :
- RSound(mixer, midiDriver, filename, dataOffset, dataSize, sysExOffset,
- kRSoundFadeCheckAlternating),
- _firstEffectChannel(firstEffectChannel) {
-}
-
-void RSoundDemo::startVoice(int channelIndex, int sequenceOffset) {
- assert(channelIndex >= 0 && channelIndex < RSOUND_CHANNEL_COUNT);
- playSoundStatic(sequenceOffset, channelIndex + 1);
-}
-
-int RSoundDemo::startVoiceInRange(int sequenceOffset, int firstChannel,
- int lastChannel) {
- assert(firstChannel >= 0 && firstChannel <= lastChannel && lastChannel < 8);
-
- for (int channel = firstChannel; channel <= lastChannel; ++channel) {
- if (!_channels[channel]._deltaCounter) {
- startVoice(channel, sequenceOffset);
- return channel;
- }
- }
-
- for (int channel = lastChannel; channel >= firstChannel; --channel) {
- if (_channels[channel]._fadeOutActive) {
- startVoice(channel, sequenceOffset);
- return channel;
- }
- }
-
- return -1;
-}
-
-int RSoundDemo::startAnyVoice(int sequenceOffset) {
- // Both demo overlays exclude rhythm channel 9 from their melodic pools.
- return startVoiceInRange(sequenceOffset, 0, 7);
-}
-
-int RSoundDemo::startEffectVoice(int sequenceOffset) {
- return startVoiceInRange(sequenceOffset, _firstEffectChannel, 7);
-}
-
-void RSoundDemo::requestStopRange(int firstChannel, int channelCount) {
- assert(firstChannel >= 0 && channelCount >= 0 &&
- firstChannel + channelCount <= RSOUND_CHANNEL_COUNT);
- for (int channel = firstChannel;
- channel < firstChannel + channelCount; ++channel)
- _channels[channel].setFadeOut(true);
-}
-
-void RSoundDemo::requestStopAll() {
- requestStopRange(0, RSOUND_CHANNEL_COUNT);
-}
-
-void RSoundDemo::stopAndResetRange(int firstChannel, int channelCount) {
- assert(firstChannel >= 0 && channelCount > 0 &&
- firstChannel + channelCount <= RSOUND_CHANNEL_COUNT);
- resetChannelRange(firstChannel + 1, firstChannel + channelCount);
- clearActiveNotesRange(firstChannel + 1, firstChannel + channelCount);
- sendMidiChannelReset(firstChannel + 1, firstChannel + channelCount);
-}
-
-void RSoundDemo::setVoiceVolume(int channelIndex, byte volume) {
- assert(channelIndex >= 0 && channelIndex < RSOUND_CHANNEL_COUNT);
- _channels[channelIndex]._volume = volume;
- sendVolume(channelIndex + 1, volume);
-}
-
-bool RSoundDemo::isSequenceActive(int sequenceOffset) {
- return isSoundPlaying(loadData(sequenceOffset));
-}
-
const RSound1::CommandPtr RSound1::_commandList[42] = {
&RSound1::command0, &RSound1::command1, &RSound1::command2, &RSound1::command3,
&RSound1::command4, &RSound1::command5, &RSound1::command6, &RSound1::command7,
@@ -370,9 +297,35 @@ int RSound1::command41() {
/*-----------------------------------------------------------------------*/
+const RSoundDemo1::CommandPtr RSoundDemo1::_commandList[41] = {
+ &RSoundDemo1::command0, &RSoundDemo1::command1, &RSoundDemo1::command2, &RSoundDemo1::command3,
+ &RSoundDemo1::command4, &RSoundDemo1::command5, &RSoundDemo1::command6, &RSoundDemo1::command7,
+ &RSoundDemo1::command8, &RSoundDemo1::command9, &RSoundDemo1::command10, &RSoundDemo1::command11,
+ &RSoundDemo1::command12, &RSoundDemo1::command13, &RSoundDemo1::command14, &RSoundDemo1::command15,
+ &RSoundDemo1::command16, &RSoundDemo1::command17, &RSoundDemo1::command18, &RSoundDemo1::command19,
+ &RSoundDemo1::command20, &RSoundDemo1::command21, &RSoundDemo1::command22, &RSoundDemo1::command23,
+ &RSoundDemo1::command24, &RSoundDemo1::command25, &RSoundDemo1::command26, &RSoundDemo1::command27,
+ &RSoundDemo1::command28, &RSoundDemo1::command29, &RSoundDemo1::command30, &RSoundDemo1::command31,
+ &RSoundDemo1::command32, &RSoundDemo1::command33, &RSoundDemo1::command34, &RSoundDemo1::command35,
+ &RSoundDemo1::command36, &RSoundDemo1::command37, &RSoundDemo1::command38, &RSoundDemo1::command39,
+ &RSoundDemo1::command40
+};
+
RSoundDemo1::RSoundDemo1(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
- RSoundDemo(mixer, midiDriver, "rsound.001", 0x12E0, 0x1D28, 0x67, 0),
+ RSound(mixer, midiDriver, "rsound.001", 0x12E0, 0x1D28, 0x67, kRSoundFadeCheckAlternating),
_command23Toggle(false) {
+ // The demo RSOUND.001 driver command 2 includes channel 5 when
+ // initializing channel data, but command 2 and 3 do not include channel 5
+ // when initializing MIDI channels and fading channels to stop. Instead,
+ // command 4 includes channel 5 when initializing channel data, but
+ // command 4 and 5 do not include channel 5 when initializing MIDI
+ // channels and fading channels to stop. This is probably a bug, which is
+ // not replicated in this reimplementation.
+ _dynamicStartChannel = 5;
+ // The demo RSOUND.001 driver processes channel 9 with the dynamic
+ // commands 4 and 5, instead of the static commands 2 and 3.
+ _dynamicIncludeChannel9 = true;
+ _staticIncludeChannel9 = false;
}
byte RSoundDemo1::adjustedCommandParam() const {
@@ -381,44 +334,14 @@ byte RSoundDemo1::adjustedCommandParam() const {
}
void RSoundDemo1::playCommand11_12_13CommonChannels() {
- if (isSequenceActive(0x1586))
+ if (isSoundPlaying(0x1586))
return;
- requestStopAll();
- startVoice(0, 0x1586);
- startVoice(1, 0x17DC);
- startVoice(2, 0x197C);
- startVoice(3, 0x19F8);
-}
-
-int RSoundDemo1::executeDemoCommonCommand(int commandId) {
- switch (commandId) {
- case 0:
- return RSound::command0();
- case 1:
- requestStopAll();
- return 0;
- case 2:
- stopAndResetRange(0, 4);
- return 0;
- case 3:
- requestStopRange(0, 4);
- return 0;
- case 4:
- stopAndResetRange(4, 5);
- return 0;
- case 5:
- requestStopRange(4, 5);
- return 0;
- case 6:
- return RSound::command6();
- case 7:
- return RSound::command7();
- case 8:
- return RSound::command8();
- default:
- return 0;
- }
+ command1();
+ playSoundStatic(0x1586, 1);
+ playSoundStatic(0x17DC, 2);
+ playSoundStatic(0x197C, 3);
+ playSoundStatic(0x19F8, 4);
}
int RSoundDemo1::command(int commandId, int param) {
@@ -427,158 +350,214 @@ int RSoundDemo1::command(int commandId, int param) {
_commandParam = param;
_ticksSinceLastCommand = 0;
- if (commandId <= 8)
- return executeDemoCommonCommand(commandId);
-
- switch (commandId) {
- case 9:
- startAnyVoice(0x0F34);
- break;
- case 10:
- if (!isSequenceActive(0x1104)) {
- requestStopAll();
- startVoice(4, 0x1104);
- startVoice(5, 0x1138);
- startVoice(6, 0x12BC);
- startVoice(7, 0x1308);
- }
- break;
- case 11:
- playCommand11_12_13CommonChannels();
- setVoiceVolume(0, 0x00);
- setVoiceVolume(1, 0x00);
- break;
- case 12:
- playCommand11_12_13CommonChannels();
- setVoiceVolume(0, 0x50);
- setVoiceVolume(1, 0x00);
- break;
- case 13:
- playCommand11_12_13CommonChannels();
- setVoiceVolume(0, 0x50);
- setVoiceVolume(1, 0x50);
- break;
- case 14:
- startAnyVoice(0x1AE2);
- break;
- case 15:
- if (!isSequenceActive(0x135A)) {
- requestStopAll();
- startVoice(4, 0x135A);
- startVoice(5, 0x144A);
- startVoice(6, 0x152E);
- }
- break;
- case 16:
- startAnyVoice(0x0F3E);
- break;
- case 17:
- startAnyVoice(0x0F48);
- break;
- case 18:
- startAnyVoice(0x0F52);
- break;
- case 19:
- requestStopAll();
- startAnyVoice(0x0F64);
- break;
- case 20:
- startAnyVoice(0x0FBE);
- break;
- case 21:
- startAnyVoice(0x0FAC);
- break;
- case 22: {
- byte *data = sequenceData(0x0FCE);
- data[6] = (generateRandomNumber() & 0x07) + 0x73;
- startAnyVoice(0x0FCE);
- break;
- }
- case 23:
- _command23Toggle = !_command23Toggle;
- startAnyVoice(_command23Toggle ? 0x0FD8 : 0x0FE0);
- break;
- case 24:
- startAnyVoice(0x0FE8);
- break;
- case 25:
- startAnyVoice(0x0FF2);
- break;
- case 26:
- case 27: {
- const int sequenceOffset = commandId == 26 ? 0x10F8 : 0x10EC;
- byte *data = sequenceData(sequenceOffset);
- data[8] = (generateRandomNumber() & 0x18) + 0x2D;
- data[5] = adjustedCommandParam() + 0x40;
- startVoice(7, sequenceOffset);
- break;
- }
- case 28:
- startAnyVoice(0x1002);
- break;
- case 29: {
- byte *data = sequenceData(0x109A);
- data[11] = (adjustedCommandParam() >> 1) + 0x20;
- if (!isSequenceActive(0x109A))
- startAnyVoice(0x109A);
- break;
- }
- case 30: {
- byte *data = sequenceData(0x10AE);
- data[11] = adjustedCommandParam() + 0x3F;
- if (!isSequenceActive(0x10AE))
- startAnyVoice(0x10AE);
- break;
- }
- case 31:
- startAnyVoice(0x1022);
- break;
- case 32: {
- const byte value = adjustedCommandParam() >> 1;
- byte *data = sequenceData(0x10C4);
- data[11] = data[23] = value + 0x44;
- data[17] = data[29] = value + 0x14;
- if (!isSequenceActive(0x10C4))
- startAnyVoice(0x10C4);
- break;
+ return (this->*_commandList[commandId])();
+}
+
+int RSoundDemo1::command9() {
+ playSoundAnyChannel(0x0F34);
+ return 0;
+}
+
+int RSoundDemo1::command10() {
+ if (!isSoundPlaying(0x1104)) {
+ command1();
+ playSoundStatic(0x1104, 5);
+ playSoundStatic(0x1138, 6);
+ playSoundStatic(0x12BC, 7);
+ playSoundStatic(0x1308, 8);
}
- case 33:
- startAnyVoice(0x1034);
- startAnyVoice(0x103E);
- break;
- case 34: {
- byte *data = sequenceData(0x104C);
- data[9] = (generateRandomNumber() & 0x0C) + 0x2D;
- data[16] = data[9] + 0x24;
- startAnyVoice(0x104C);
- break;
+ return 0;
+}
+
+int RSoundDemo1::command11() {
+ playCommand11_12_13CommonChannels();
+ setChannelVolume(1, 0x00);
+ setChannelVolume(2, 0x00);
+ return 0;
+}
+
+int RSoundDemo1::command12() {
+ playCommand11_12_13CommonChannels();
+ setChannelVolume(1, 0x50);
+ setChannelVolume(2, 0x00);
+ return 0;
+}
+
+int RSoundDemo1::command13() {
+ playCommand11_12_13CommonChannels();
+ setChannelVolume(1, 0x50);
+ setChannelVolume(2, 0x50);
+ return 0;
+}
+
+int RSoundDemo1::command14() {
+ playSoundAnyChannel(0x1AE2);
+ return 0;
+}
+
+int RSoundDemo1::command15() {
+ if (!isSoundPlaying(0x135A)) {
+ command1();
+ playSoundStatic(0x135A, 5);
+ playSoundStatic(0x144A, 6);
+ playSoundStatic(0x152E, 7);
}
- case 35:
- startAnyVoice(0x1060);
- break;
- case 36:
- startAnyVoice(0x1078);
- break;
- case 37:
- startAnyVoice(0x1086);
- break;
- case 38:
- startAnyVoice(0x1090);
- break;
- case 39:
- if (!isSequenceActive(0x1C38)) {
- startVoice(4, 0x1C38);
- startVoice(5, 0x1C68);
- startVoice(6, 0x1C94);
- startVoice(7, 0x1CD4);
- startVoice(8, 0x1CEE);
- }
- break;
- case 40:
- startAnyVoice(0x106E);
- break;
+ return 0;
+}
+
+int RSoundDemo1::command16() {
+ playSoundAnyChannel(0x0F3E);
+ return 0;
+}
+
+int RSoundDemo1::command17() {
+ playSoundAnyChannel(0x0F48);
+ return 0;
+}
+
+int RSoundDemo1::command18() {
+ playSoundAnyChannel(0x0F52);
+ return 0;
+}
+
+int RSoundDemo1::command19() {
+ command1();
+ playSoundAnyChannel(0x0F64);
+ return 0;
+}
+
+int RSoundDemo1::command20() {
+ playSoundAnyChannel(0x0FBE);
+ return 0;
+}
+
+int RSoundDemo1::command21() {
+ playSoundAnyChannel(0x0FAC);
+ return 0;
+}
+
+int RSoundDemo1::command22() {
+ byte *data = loadData(0x0FCE);
+ data[6] = (generateRandomNumber() & 0x07) + 0x73;
+ playSoundAnyChannel(0x0FCE);
+ return 0;
+}
+
+int RSoundDemo1::command23() {
+ _command23Toggle = !_command23Toggle;
+ playSoundAnyChannel(_command23Toggle ? 0x0FD8 : 0x0FE0);
+ return 0;
+}
+
+int RSoundDemo1::command24() {
+ playSoundAnyChannel(0x0FE8);
+ return 0;
+}
+
+int RSoundDemo1::command25() {
+ playSoundAnyChannel(0x0FF2);
+ return 0;
+}
+
+int RSoundDemo1::patchAndPlaySound(int offset) {
+ byte *data = loadData(offset);
+ data[8] = (generateRandomNumber() & 0x18) + 0x2D;
+ data[5] = adjustedCommandParam() + 0x40;
+ playSoundStatic(data, 8);
+ return 0;
+}
+
+int RSoundDemo1::command26() {
+ return patchAndPlaySound(0x10F8);
+}
+
+int RSoundDemo1::command27() {
+ return patchAndPlaySound(0x10EC);
+}
+
+int RSoundDemo1::command28() {
+ playSoundAnyChannel(0x1002);
+ return 0;
+}
+
+int RSoundDemo1::command29() {
+ byte *data = loadData(0x109A);
+ data[11] = (adjustedCommandParam() >> 1) + 0x20;
+ if (!isSoundPlaying(0x109A))
+ playSoundAnyChannel(0x109A);
+ return 0;
+}
+
+int RSoundDemo1::command30() {
+ byte *data = loadData(0x10AE);
+ data[11] = adjustedCommandParam() + 0x3F;
+ if (!isSoundPlaying(0x10AE))
+ playSoundAnyChannel(0x10AE);
+ return 0;
+}
+
+int RSoundDemo1::command31() {
+ playSoundAnyChannel(0x1022);
+ return 0;
+}
+
+int RSoundDemo1::command32() {
+ const byte value = adjustedCommandParam() >> 1;
+ byte *data = loadData(0x10C4);
+ data[11] = data[23] = value + 0x44;
+ data[17] = data[29] = value + 0x14;
+ if (!isSoundPlaying(0x10C4))
+ playSoundAnyChannel(0x10C4);
+ return 0;
+}
+
+int RSoundDemo1::command33() {
+ playSoundAnyChannel(0x1034);
+ playSoundAnyChannel(0x103E);
+ return 0;
+}
+
+int RSoundDemo1::command34() {
+ byte *data = loadData(0x104C);
+ data[9] = (generateRandomNumber() & 0x0C) + 0x2D;
+ data[16] = data[9] + 0x24;
+ playSoundAnyChannel(0x104C);
+ return 0;
+}
+
+int RSoundDemo1::command35() {
+ playSoundAnyChannel(0x1060);
+ return 0;
+}
+
+int RSoundDemo1::command36() {
+ playSoundAnyChannel(0x1078);
+ return 0;
+}
+
+int RSoundDemo1::command37() {
+ playSoundAnyChannel(0x1086);
+ return 0;
+}
+
+int RSoundDemo1::command38() {
+ playSoundAnyChannel(0x1090);
+ return 0;
+}
+
+int RSoundDemo1::command39() {
+ if (!isSoundPlaying(0x1C38)) {
+ playSoundStatic(0x1C38, 5);
+ playSoundStatic(0x1C68, 6);
+ playSoundStatic(0x1C94, 7);
+ playSoundStatic(0x1CD4, 8);
+ playSoundStatic(0x1CEE, 9);
}
+ return 0;
+}
+int RSoundDemo1::command40() {
+ playSoundAnyChannel(0x106E);
return 0;
}
@@ -2674,40 +2653,27 @@ int RSound9::command51() {
/*-----------------------------------------------------------------------*/
-RSoundDemo9::RSoundDemo9(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
- RSoundDemo(mixer, midiDriver, "rsound.009", 0x11D0, 0x3664, 0x69, 5) {
-}
+const RSoundDemo9::CommandPtr RSoundDemo9::_commandList[40] = {
+ &RSoundDemo9::command0, &RSoundDemo9::command1, &RSoundDemo9::command2, &RSoundDemo9::command3,
+ &RSoundDemo9::command4, &RSoundDemo9::command5, &RSoundDemo9::command6, &RSoundDemo9::command7,
+ &RSoundDemo9::command8, &RSoundDemo9::nullCommand, &RSoundDemo9::nullCommand, &RSoundDemo9::command11,
+ &RSoundDemo9::command12, &RSoundDemo9::command13, &RSoundDemo9::command14, &RSoundDemo9::command15,
+ &RSoundDemo9::command16, &RSoundDemo9::command17, &RSoundDemo9::command18, &RSoundDemo9::command19,
+ &RSoundDemo9::command20, &RSoundDemo9::command21, &RSoundDemo9::command22, &RSoundDemo9::command23,
+ &RSoundDemo9::command24, &RSoundDemo9::command25, &RSoundDemo9::command26, &RSoundDemo9::command27,
+ &RSoundDemo9::command28, &RSoundDemo9::command29, &RSoundDemo9::command30, &RSoundDemo9::command31,
+ &RSoundDemo9::command32, &RSoundDemo9::command33, &RSoundDemo9::command34_39, &RSoundDemo9::command35,
+ &RSoundDemo9::command36, &RSoundDemo9::command37, &RSoundDemo9::command38, &RSoundDemo9::command34_39
+};
-int RSoundDemo9::executeDemoCommonCommand(int commandId) {
- switch (commandId) {
- case 0:
- return RSound::command0();
- case 1:
- requestStopRange(0, 9);
- return 0;
- case 2:
- stopAndResetRange(0, 5);
- // The opening overlay repeats its first embedded DT1 record here.
- sendSysEx(0x69);
- return 0;
- case 3:
- requestStopRange(0, 5);
- return 0;
- case 4:
- stopAndResetRange(5, 4);
- return 0;
- case 5:
- requestStopRange(5, 4);
- return 0;
- case 6:
- return RSound::command6();
- case 7:
- return RSound::command7();
- case 8:
- return RSound::command8();
- default:
- return 0;
- }
+RSoundDemo9::RSoundDemo9(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
+ RSound(mixer, midiDriver, "rsound.009", 0x11D0, 0x3664, 0x69, kRSoundFadeCheckAlternating) {
+ // The demo RSOUND.009 driver uses channel range 1-5 for the static init
+ // and stop commands 2 and 3, and 6-9 for the dynamic init and stop
+ // commands 4 and 5.
+ _dynamicStartChannel = 6;
+ _dynamicIncludeChannel9 = true;
+ _staticIncludeChannel9 = false;
}
int RSoundDemo9::command(int commandId, int param) {
@@ -2716,138 +2682,182 @@ int RSoundDemo9::command(int commandId, int param) {
_commandParam = param;
_ticksSinceLastCommand = 0;
- if (commandId <= 8)
- return executeDemoCommonCommand(commandId);
-
- switch (commandId) {
- case 9:
- case 10:
- break;
- case 11:
- startVoice(7, 0x1454);
- break;
- case 12:
- startVoice(7, 0x14A0);
- break;
- case 13:
- startVoice(7, 0x14AC);
- break;
- case 14:
- startVoice(7, 0x14B4);
- break;
- case 15:
- startVoice(7, 0x14D4);
- break;
- case 16:
- startVoice(7, 0x14EC);
- break;
- case 17:
- startVoice(7, 0x14E2);
- break;
- case 18:
- startEffectVoice(0x12BA);
- break;
- case 19:
- startEffectVoice(0x12D4);
- break;
- case 20: {
- byte *data = sequenceData(0x12F6);
- data[6] = (byte)(((generateRandomNumber() & 0x38) + 0x4D) & 0x7F);
- startEffectVoice(0x12F6);
- break;
- }
- case 21:
- case 22: {
- byte *data = sequenceData(0x130A);
- data[9] = commandId == 21 ? 0x46 : 0x2D;
- if (!isSequenceActive(0x130A))
- startEffectVoice(0x130A);
- break;
- }
- case 23: {
- static const int sequences[] = { 0x1322, 0x1328, 0x133A };
- for (uint index = 0; index < 3; ++index) {
- const int channel = startEffectVoice(sequences[index]);
- if (channel >= 0)
- voice(channel)._innerLoopStart = loadData(0x1340);
- }
- break;
- }
- case 24:
- startEffectVoice(0x1352);
- break;
- case 25:
- startEffectVoice(0x1368);
- break;
- case 26:
- startEffectVoice(0x138C);
- break;
- case 27:
- startEffectVoice(0x13A4);
- break;
- case 28: {
- byte *data = sequenceData(0x13BC);
- data[6] = (byte)(((generateRandomNumber() & 0x1C) + 0x0F) & 0x7F);
- startEffectVoice(0x13BC);
- break;
- }
- case 29: {
- byte *data = sequenceData(0x13D0);
- data[6] = (byte)(((generateRandomNumber() & 0x0C) + 0x21) & 0x7F);
- startEffectVoice(0x13D0);
- break;
- }
- case 30:
- startEffectVoice(0x13F8);
- break;
- case 31:
- startEffectVoice(0x1408);
- startEffectVoice(0x1416);
- startEffectVoice(0x1424);
- break;
- case 32:
- startEffectVoice(0x1432);
- break;
- case 33:
- startEffectVoice(0x143C);
- break;
- case 34:
- case 39:
- startVoice(0, 0x1522);
- startVoice(1, 0x1700);
- startVoice(2, 0x1892);
- startVoice(3, 0x21F2);
- startVoice(4, 0x2E4A);
- break;
- case 35:
- startEffectVoice(0x14BE);
- break;
- case 36: {
- startEffectVoice(0x13BC);
-
- int channel = startEffectVoice(0x1334);
- if (channel >= 0)
- voice(channel)._innerLoopStart = loadData(0x13EA);
-
- channel = startEffectVoice(0x132E);
- if (channel >= 0)
- voice(channel)._innerLoopStart = loadData(0x13DA);
- break;
- }
- case 37: {
- byte *data = sequenceData(0x150E);
- data[6] = (byte)(((generateRandomNumber() & 0x02) + 0x48) & 0x7F);
- startEffectVoice(0x150E);
- break;
- }
- case 38:
- startVoice(0, 0x35C4);
- startVoice(1, 0x35CE);
- startVoice(2, 0x3612);
- startVoice(3, 0x3656);
- break;
+ return (this->*_commandList[commandId])();
+}
+
+int RSoundDemo9::command11() {
+ playSoundStatic(0x1454, 8);
+ return 0;
+}
+
+int RSoundDemo9::command12() {
+ playSoundStatic(0x14A0, 8);
+ return 0;
+}
+
+int RSoundDemo9::command13() {
+ playSoundStatic(0x14AC, 8);
+ return 0;
+}
+
+int RSoundDemo9::command14() {
+ playSoundStatic(0x14B4, 8);
+ return 0;
+}
+
+int RSoundDemo9::command15() {
+ playSoundStatic(0x14D4, 8);
+ return 0;
+}
+
+int RSoundDemo9::command16() {
+ playSoundStatic(0x14EC, 8);
+ return 0;
+}
+
+int RSoundDemo9::command17() {
+ playSoundStatic(0x14E2, 8);
+ return 0;
+}
+
+int RSoundDemo9::command18() {
+ playSoundDynamic(0x12BA);
+ return 0;
+}
+
+int RSoundDemo9::command19() {
+ playSoundDynamic(0x12D4);
+ return 0;
+}
+
+int RSoundDemo9::command20() {
+ byte *data = loadData(0x12F6);
+ data[6] = (byte)(((generateRandomNumber() & 0x38) + 0x4D) & 0x7F);
+ playSoundDynamic(0x12F6);
+ return 0;
+}
+
+int RSoundDemo9::patchAndPlaySound(byte param) {
+ byte *data = loadData(0x130A);
+ data[9] = param;
+ if (!isSoundPlaying(0x130A))
+ playSoundDynamic(0x130A);
+ return 0;
+}
+
+int RSoundDemo9::command21() {
+ return patchAndPlaySound(0x46);
+}
+
+int RSoundDemo9::command22() {
+ return patchAndPlaySound(0x2D);
+}
+
+int RSoundDemo9::command23() {
+ static const int sequences[] = { 0x1322, 0x1328, 0x133A };
+ for (uint index = 0; index < 3; ++index) {
+ Channel *channel = playSoundDynamic(sequences[index]);
+ if (channel != nullptr)
+ channel->_innerLoopStart = loadData(0x1340);
}
+ return 0;
+}
+
+int RSoundDemo9::command24() {
+ playSoundDynamic(0x1352);
+ return 0;
+}
+
+int RSoundDemo9::command25() {
+ playSoundDynamic(0x1368);
+ return 0;
+}
+
+int RSoundDemo9::command26() {
+ playSoundDynamic(0x138C);
+ return 0;
+}
+
+int RSoundDemo9::command27() {
+ playSoundDynamic(0x13A4);
+ return 0;
+}
+
+int RSoundDemo9::command28() {
+ byte *data = loadData(0x13BC);
+ data[6] = (byte)(((generateRandomNumber() & 0x1C) + 0x0F) & 0x7F);
+ playSoundDynamic(0x13BC);
+ return 0;
+}
+
+int RSoundDemo9::command29() {
+ byte *data = loadData(0x13D0);
+ data[6] = (byte)(((generateRandomNumber() & 0x0C) + 0x21) & 0x7F);
+ playSoundDynamic(0x13D0);
+ return 0;
+}
+
+int RSoundDemo9::command30() {
+ playSoundDynamic(0x13F8);
+ return 0;
+}
+
+int RSoundDemo9::command31() {
+ playSoundDynamic(0x1408);
+ playSoundDynamic(0x1416);
+ playSoundDynamic(0x1424);
+ return 0;
+}
+
+int RSoundDemo9::command32() {
+ playSoundDynamic(0x1432);
+ return 0;
+}
+
+int RSoundDemo9::command33() {
+ playSoundDynamic(0x143C);
+ return 0;
+}
+
+int RSoundDemo9::command34_39() {
+ playSoundStatic(0x1522, 1);
+ playSoundStatic(0x1700, 2);
+ playSoundStatic(0x1892, 3);
+ playSoundStatic(0x21F2, 4);
+ playSoundStatic(0x2E4A, 5);
+ return 0;
+}
+
+int RSoundDemo9::command35() {
+ playSoundDynamic(0x14BE);
+ return 0;
+}
+
+int RSoundDemo9::command36() {
+ playSoundDynamic(0x13BC);
+
+ Channel *channel = playSoundDynamic(0x1334);
+ if (channel != nullptr)
+ channel->_innerLoopStart = loadData(0x13EA);
+
+ channel = playSoundDynamic(0x132E);
+ if (channel != nullptr)
+ channel->_innerLoopStart = loadData(0x13DA);
+ return 0;
+}
+
+int RSoundDemo9::command37() {
+ byte *data = loadData(0x150E);
+ data[6] = (byte)(((generateRandomNumber() & 0x02) + 0x48) & 0x7F);
+ playSoundDynamic(0x150E);
+ return 0;
+}
+int RSoundDemo9::command38() {
+ playSoundStatic(0x35C4, 1);
+ playSoundStatic(0x35CE, 2);
+ playSoundStatic(0x3612, 3);
+ playSoundStatic(0x3656, 4);
return 0;
}
diff --git a/engines/mads/nebular/sound/rsound_nebular.h b/engines/mads/nebular/sound/rsound_nebular.h
index 63ffa6ea8ee..2e5f2c40a94 100644
--- a/engines/mads/nebular/sound/rsound_nebular.h
+++ b/engines/mads/nebular/sound/rsound_nebular.h
@@ -28,30 +28,6 @@ namespace MADS {
namespace RexNebular {
namespace Sound {
-/** Shared mechanics of the two distinct Rex demo Roland overlays. */
-class RSoundDemo : public RSound {
-private:
- int _firstEffectChannel;
-
-protected:
- RSoundDemo(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver, const Common::Path &filename,
- int dataOffset, int dataSize, int sysExOffset,
- int firstEffectChannel);
-
- void startVoice(int channelIndex, int sequenceOffset);
- int startVoiceInRange(int sequenceOffset, int firstChannel,
- int lastChannel);
- int startAnyVoice(int sequenceOffset);
- int startEffectVoice(int sequenceOffset);
- void requestStopRange(int firstChannel, int channelCount);
- void requestStopAll();
- void stopAndResetRange(int firstChannel, int channelCount);
- void setVoiceVolume(int channelIndex, byte volume);
- bool isSequenceActive(int sequenceOffset);
- byte *sequenceData(int sequenceOffset) { return loadData(sequenceOffset); }
- Channel &voice(int channelIndex) { return _channels[channelIndex]; }
-};
-
class RSound1 : public RSound {
private:
typedef int (RSound1:: *CommandPtr)();
@@ -110,16 +86,53 @@ public:
};
/** Demo RSOUND.001: `RLND AGAdemo 6-11-92`; 41 commands. */
-class RSoundDemo1 : public RSoundDemo {
+class RSoundDemo1 : public RSound {
private:
+ typedef int (RSoundDemo1::*CommandPtr)();
+ static const CommandPtr _commandList[41];
+
bool _command23Toggle;
byte adjustedCommandParam() const;
void playCommand11_12_13CommonChannels();
- int executeDemoCommonCommand(int commandId);
+ int patchAndPlaySound(int offset);
+
+ int command9();
+ int command10();
+ int command11();
+ int command12();
+ int command13();
+ int command14();
+ int command15();
+ int command16();
+ int command17();
+ int command18();
+ int command19();
+ int command20();
+ int command21();
+ int command22();
+ int command23();
+ int command24();
+ int command25();
+ int command26();
+ int command27();
+ int command28();
+ int command29();
+ int command30();
+ int command31();
+ int command32();
+ int command33();
+ int command34();
+ int command35();
+ int command36();
+ int command37();
+ int command38();
+ int command39();
+ int command40();
public:
- explicit RSoundDemo1(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver);
+ RSoundDemo1(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver);
+
int command(int commandId, int param) override;
};
@@ -623,12 +636,47 @@ public:
};
/** Demo RSOUND.009: `RLND AGAdemo 6-25-92`; 40 commands. */
-class RSoundDemo9 : public RSoundDemo {
+class RSoundDemo9 : public RSound {
private:
- int executeDemoCommonCommand(int commandId);
+ typedef int (RSoundDemo9::*CommandPtr)();
+ static const CommandPtr _commandList[40];
+
+ int patchAndPlaySound(byte param);
+
+ int command9();
+ int command10();
+ int command11();
+ int command12();
+ int command13();
+ int command14();
+ int command15();
+ int command16();
+ int command17();
+ int command18();
+ int command19();
+ int command20();
+ int command21();
+ int command22();
+ int command23();
+ int command24();
+ int command25();
+ int command26();
+ int command27();
+ int command28();
+ int command29();
+ int command30();
+ int command31();
+ int command32();
+ int command33();
+ int command34_39();
+ int command35();
+ int command36();
+ int command37();
+ int command38();
public:
- explicit RSoundDemo9(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver);
+ RSoundDemo9(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver);
+
int command(int commandId, int param) override;
};
Commit: 1acb7ec2a1f91192369106e8d915ca037d904063
https://github.com/scummvm/scummvm/commit/1acb7ec2a1f91192369106e8d915ca037d904063
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-10-09T18:19:49+02:00
Commit Message:
MADS: Rex MT-32 music / SFX volume control
Changed paths:
engines/mads/nebular/sound/rsound.cpp
engines/mads/nebular/sound/rsound.h
engines/mads/nebular/sound/rsound_nebular.cpp
engines/mads/nebular/sound/rsound_nebular.h
diff --git a/engines/mads/nebular/sound/rsound.cpp b/engines/mads/nebular/sound/rsound.cpp
index 661992c348e..204e74128c0 100644
--- a/engines/mads/nebular/sound/rsound.cpp
+++ b/engines/mads/nebular/sound/rsound.cpp
@@ -29,7 +29,7 @@ namespace MADS {
namespace RexNebular {
namespace Sound {
-void Channel::loadData(byte *soundData) {
+void Channel::loadData(byte *soundData, byte source) {
_pitchSlideStepSize = 0;
_panningSweepStepSize = 0;
_innerLoopCounter = 0;
@@ -44,6 +44,8 @@ void Channel::loadData(byte *soundData) {
_innerLoopStart = soundData;
_outerLoopStart = soundData;
_soundData = soundData;
+
+ _source = source;
}
void Channel::setFadeOut(bool fadeOut) {
@@ -77,15 +79,17 @@ RSound::RSound(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver, const Common:
_fadeOutSpeed = (_fadeOutCheckMode == kRSoundFadeCheckAlternating ? 2 : 0);
_fadeOutCounter = _fadeOutSpeed;
+ // The default settings for these fields match those used by RSOUND.003-008
+ // of the full version of Rex: static channels 1-4 and 9 and dynamic
+ // channels 5-8.
+ // There is a (probable) bug in the implementation of commands 2 and 4 of
+ // RSOUND.003-008. Command 2 includes channel 9 when stopping channel
+ // playback, but does not include channel 9 when initializing MIDI
+ // channels. Conversely, command 4 does not include channel 9 when stopping
+ // playback, but does include channel 9 when initializing MIDI channels.
+ // This inconsistency is not replicated in this reimplementation.
_dynamicStartChannel = 5;
_dynamicIncludeChannel9 = false;
- // The full game RSOUND drivers command 2 and 3 include channel 9 when
- // initializing channel data and fading channels to stop, but command 2
- // does not include channel 9 when initializing MIDI channels. Instead,
- // command 4 includes channel 9 when initializing MIDI channels, but
- // command 4 and 5 do not include channel 9 when initializing channel
- // data and fading channels to stop. This is probably a bug, which is
- // not replicated in this reimplementation.
_staticIncludeChannel9 = true;
for (int i = 0; i < RSOUND_CHANNEL_COUNT; ++i) {
@@ -175,28 +179,28 @@ Channel *RSound::getChannel(byte channel) {
return &_channels[channel - 1];
}
-Channel *RSound::playSoundStatic(byte *soundData, byte channel) {
+Channel *RSound::playSoundStatic(byte *soundData, byte channel, byte source) {
assert(channel >= 1 && channel <= RSOUND_CHANNEL_COUNT);
Channel *chan = getChannel(channel);
- Channel_playData(chan, soundData);
+ Channel_playData(chan, soundData, source);
return chan;
}
-Channel *RSound::playSoundStatic(int offset, byte channel) {
- return playSoundStatic(loadData(offset), channel);
+Channel *RSound::playSoundStatic(int offset, byte channel, byte source) {
+ return playSoundStatic(loadData(offset), channel, source);
}
-Channel *RSound::playSoundDynamic(int offset) {
- return allocateAndPlay(loadData(offset), _dynamicStartChannel);
+Channel *RSound::playSoundDynamic(int offset, byte source) {
+ return allocateAndPlay(loadData(offset), _dynamicStartChannel, source);
}
-Channel *RSound::playSoundAnyChannel(int offset) {
- return allocateAndPlay(loadData(offset), 1);
+Channel *RSound::playSoundAnyChannel(int offset, byte source) {
+ return allocateAndPlay(loadData(offset), 1, source);
}
-Channel *RSound::allocateAndPlay(byte *pData, int startingChannel) {
+Channel *RSound::allocateAndPlay(byte *pData, int startingChannel, byte source) {
int endChannel = RSOUND_CHANNEL_COUNT - 1;
Channel *foundChannel = nullptr;
@@ -220,7 +224,7 @@ Channel *RSound::allocateAndPlay(byte *pData, int startingChannel) {
}
if (foundChannel != nullptr)
- Channel_playData(foundChannel, pData);
+ Channel_playData(foundChannel, pData, source);
return foundChannel;
}
@@ -257,25 +261,26 @@ int RSound::getTicksSinceLastCommand() {
/*-----------------------------------------------------------------------*/
void RSound::sendNoteOn(byte midiChannel, byte note, byte velocity) {
- _midiDriver->send(MidiDriver::MIDI_COMMAND_NOTE_ON | midiChannel, note, velocity);
+ _midiDriver->send(getChannel(midiChannel)->_source, MidiDriver::MIDI_COMMAND_NOTE_ON | midiChannel, note, velocity);
}
void RSound::sendProgramChange(byte midiChannel, byte program) {
- _midiDriver->send(MidiDriver::MIDI_COMMAND_PROGRAM_CHANGE | midiChannel, program, 0);
+ _midiDriver->send(getChannel(midiChannel)->_source, MidiDriver::MIDI_COMMAND_PROGRAM_CHANGE | midiChannel, program, 0);
}
void RSound::sendVolume(byte midiChannel, byte volume) {
- _midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | midiChannel,
+ _midiDriver->send(getChannel(midiChannel)->_source,
+ MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | midiChannel,
MidiDriver::MIDI_CONTROLLER_VOLUME, volume);
}
void RSound::sendPitchBend(byte midiChannel, byte value) {
// LSB always 0 - only coarse (MSB) control is used
- _midiDriver->send(MidiDriver::MIDI_COMMAND_PITCH_BEND | midiChannel, 0, value);
+ _midiDriver->send(getChannel(midiChannel)->_source, MidiDriver::MIDI_COMMAND_PITCH_BEND | midiChannel, 0, value);
}
void RSound::sendPanning(byte midiChannel, byte value) {
- _midiDriver->send(MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | midiChannel, MidiDriver::MIDI_CONTROLLER_PANNING, value);
+ _midiDriver->send(getChannel(midiChannel)->_source, MidiDriver::MIDI_COMMAND_CONTROL_CHANGE | midiChannel, MidiDriver::MIDI_CONTROLLER_PANNING, value);
}
void RSound::muteChannel(byte midiChannel) {
@@ -348,11 +353,11 @@ void RSound::stopAllNotes() {
/*-----------------------------------------------------------------------*/
-void RSound::Channel_playData(Channel *channel, byte *soundData) {
+void RSound::Channel_playData(Channel *channel, byte *soundData, byte source) {
bool ticksProcessingDisabled = _ticksProcessingDisabled;
_ticksProcessingDisabled = true;
- channel->loadData(soundData);
+ channel->loadData(soundData, source);
channel->_deltaCounter = 1;
sendPitchBend(channel->_midiChannel, 0x40);
@@ -621,7 +626,7 @@ void RSound::Channel_processTick(Channel *channel) {
// Reload data to completely reset channel state, but keep
// fade-out to stop state.
bool fadeOutActive = channel->_fadeOutActive;
- channel->loadData(channel->_soundDataStart);
+ channel->loadData(channel->_soundDataStart, channel->_source);
channel->_fadeOutActive = fadeOutActive;
break;
}
@@ -780,12 +785,12 @@ void RSound::processTick() {
/*-----------------------------------------------------------------------*/
/**
- * Zeroes _deltaCounter and the three fade-step fields for channels in
- * [first, last].
+ * Stops playback and effects processing by zeroing _deltaCounter and the
+ * three step size fields for channels in [first, last].
* Deliberately does NOT touch the loop pointers, volume, program, pan etc,
* matching the original.
*/
-void RSound::resetChannelRange(int firstChannel, int lastChannel, bool includeChannel9) {
+void RSound::stopChannelRange(int firstChannel, int lastChannel, bool includeChannel9) {
_ticksProcessingDisabled = true;
for (int i = firstChannel; i <= lastChannel; ++i) {
@@ -831,7 +836,7 @@ byte *RSound::loadData(int offset) {
* command0.
*/
void RSound::resetAllChannels() {
- resetChannelRange(1, RSOUND_CHANNEL_COUNT);
+ stopChannelRange(1, RSOUND_CHANNEL_COUNT);
clearActiveNotes();
}
@@ -878,9 +883,9 @@ int RSound::command1() {
}
int RSound::command2() {
- // Initialize the static channels (also reinitializes the active notes
- // table) plus the MIDI channel reset for those same channels.
- resetChannelRange(1, _dynamicStartChannel - 1, _staticIncludeChannel9);
+ // Stops playback on the static channels (also reinitializes the active
+ // notes table) plus the MIDI channel reset for those same channels.
+ stopChannelRange(1, _dynamicStartChannel - 1, _staticIncludeChannel9);
clearActiveNotes();
setFadeOutSpeed(0);
sendMidiChannelReset(1, _dynamicStartChannel - 1, _staticIncludeChannel9);
@@ -899,9 +904,9 @@ int RSound::command3() {
}
int RSound::command4() {
- // Initialize the dynamic Channels (does NOT touch the active notes
+ // Stops playback on the dynamic Channels (does NOT touch the active notes
// table) plus the MIDI channel reset for those same channels.
- resetChannelRange(_dynamicStartChannel, 8, _dynamicIncludeChannel9);
+ stopChannelRange(_dynamicStartChannel, 8, _dynamicIncludeChannel9);
setFadeOutSpeed(0);
sendMidiChannelReset(_dynamicStartChannel, 8, _dynamicIncludeChannel9);
return 0;
diff --git a/engines/mads/nebular/sound/rsound.h b/engines/mads/nebular/sound/rsound.h
index 998a3385272..201072e0b07 100644
--- a/engines/mads/nebular/sound/rsound.h
+++ b/engines/mads/nebular/sound/rsound.h
@@ -70,7 +70,7 @@ enum RSoundFadeCheckMode {
* 0x09 _volumeFadeCounter 0x16 _pSrc
* 0x0A _pitchSlideCounter
* 0x0B _panningSweepCounter
- * _owner and _midiChannel below are NOT part of the original struct (it
+ * _source and _midiChannel below are NOT part of the original struct (it
* has no equivalent fields) - they're C++-side conveniences so Channel
* methods and callers don't need the MIDI channel number (array index+1)
* threaded through separately. Any future raw "[bx+N]" disassembly offset
@@ -79,6 +79,7 @@ enum RSoundFadeCheckMode {
class Channel {
public:
byte _midiChannel = 0; // 1-9: the MIDI channel to which the data in this struct pertains
+ byte _source = 0; // The source used for the multisource driver (0: music, 1: SFX)
byte _deltaCounter = 0; // number of ticks until the next event occurs; loaded from the delta byte of a note or chord event
// 0: channel is not active
@@ -119,7 +120,7 @@ public:
* touch volume, program, velocity, key-on state or delta/duration -
* matches the original disassembly.
*/
- void loadData(byte *soundData);
+ void loadData(byte *soundData, byte source);
/**
* Marks the channel as fading out (fading toward silence) and stopping
@@ -205,7 +206,7 @@ private:
* Loads new sound data into the channel, starts playback by setting
* _deltaCounter to 1 and resets pitch bend to center on the MT-32.
*/
- void Channel_playData(Channel *channel, byte *soundData);
+ void Channel_playData(Channel *channel, byte *soundData, byte source);
/**
* Sends Note-Off (velocity 0) for all active notes on the
@@ -224,6 +225,8 @@ protected:
// Channel 9 can be included with the static or dynamic channels by setting
// the corresponding boolean field. This will only affect commands 2-5;
// channel 9 is never used by playSoundDynamic.
+ // There are a number of variations of these settings in the RSOUND.00x
+ // drivers used by Rex.
byte _dynamicStartChannel;
bool _staticIncludeChannel9;
bool _dynamicIncludeChannel9;
@@ -231,10 +234,11 @@ protected:
void setFadeOutSpeed(int fadeOutSpeed);
/**
- * Clear the active and fade state for MIDI channels in [first, last].
- * Specify includeChannel9 to also reset MIDI channel 9.
+ * Stop playback by clearing the delta and effects counters for MIDI
+ * channels in [first, last]. Specify includeChannel9 to also reset MIDI
+ * channel 9.
*/
- void resetChannelRange(int firstChannel, int lastChannel, bool includeChannel9 = false);
+ void stopChannelRange(int firstChannel, int lastChannel, bool includeChannel9 = false);
/** Reset the per-MIDI-channel active note tracking table to empty. */
void clearActiveNotes();
@@ -283,8 +287,8 @@ protected:
* channel. This will unload any sound already playing on the
* channel.
*/
- Channel *playSoundStatic(int offset, byte channel);
- Channel *playSoundStatic(byte *soundData, byte channel);
+ Channel *playSoundStatic(int offset, byte channel, byte source = 0);
+ Channel *playSoundStatic(byte *soundData, byte channel, byte source = 0);
/**
* Play the specified sound by allocating one of the free dynamic
@@ -292,13 +296,13 @@ protected:
* was free), since some commands poke the just-loaded channel's
* loop pointer directly afterward.
*/
- Channel *playSoundDynamic(int offset);
+ Channel *playSoundDynamic(int offset, byte source = 1);
/**
* Play the specified sound by allocating any free melodic channel,
* dynamic or static.
*/
- Channel *playSoundAnyChannel(int offset);
+ Channel *playSoundAnyChannel(int offset, byte source = 1);
/**
* Dynamically allocates a melodic channel, loads the specified sound
@@ -308,7 +312,7 @@ protected:
* could be found, nullptr is returned and the sound data is not
* played.
*/
- Channel *allocateAndPlay(byte *pData, int startingChannel);
+ Channel *allocateAndPlay(byte *pData, int startingChannel, byte source);
/**
* Checks to see whether the given block of data is already loaded
@@ -382,7 +386,7 @@ protected:
int command2();
int command3();
int command4();
- int command5();
+ virtual int command5();
int command6();
int command7();
int command8();
diff --git a/engines/mads/nebular/sound/rsound_nebular.cpp b/engines/mads/nebular/sound/rsound_nebular.cpp
index 7a8071f03a8..64e1f2c843c 100644
--- a/engines/mads/nebular/sound/rsound_nebular.cpp
+++ b/engines/mads/nebular/sound/rsound_nebular.cpp
@@ -40,11 +40,19 @@ const RSound1::CommandPtr RSound1::_commandList[42] = {
};
RSound1::RSound1(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
- RSound(mixer, midiDriver, "rsound.001", 0x1350, 0x1A90, 0x67, kRSoundFadeCheckAlternating) {
+ RSound(mixer, midiDriver, "rsound.001", 0x1350, 0x1A90, 0x67, kRSoundFadeCheckAlternating) {
+ // Rex full game RSOUND.001 includes channel 9 with the dynamic channel
+ // commands 4 and 5.
+ // The original code actually uses channels 4-8 instead of 5-8 for
+ // playSoundDynamic, which is inconsistent with command 2-5. This
+ // inconsistency has not been replicated in this reimplementation for now.
+ _dynamicStartChannel = 5;
+ _dynamicIncludeChannel9 = true;
+ _staticIncludeChannel9 = false;
}
int RSound1::command(int commandId, int param) {
- if (commandId > 41)
+ if (commandId < 0 || commandId > 41)
return 0;
_commandParam = param;
@@ -105,7 +113,7 @@ int RSound1::command13() {
}
int RSound1::command14() {
- playSoundDynamic(0x16C2);
+ playSoundDynamic(0x16C2, 0);
return 0;
}
@@ -179,7 +187,7 @@ int RSound1::command26() {
pData[8] = v1;
int v2 = clampParam() + 64;
pData[5] = v2;
- playSoundStatic(pData, 8);
+ playSoundStatic(pData, 8, 1);
return 0;
}
@@ -189,7 +197,7 @@ int RSound1::command27() {
pData[8] = v1;
int v2 = clampParam() + 64;
pData[5] = v2;
- playSoundStatic(pData, 8);
+ playSoundStatic(pData, 8, 1);
return 0;
}
@@ -314,16 +322,17 @@ const RSoundDemo1::CommandPtr RSoundDemo1::_commandList[41] = {
RSoundDemo1::RSoundDemo1(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
RSound(mixer, midiDriver, "rsound.001", 0x12E0, 0x1D28, 0x67, kRSoundFadeCheckAlternating),
_command23Toggle(false) {
- // The demo RSOUND.001 driver command 2 includes channel 5 when
- // initializing channel data, but command 2 and 3 do not include channel 5
- // when initializing MIDI channels and fading channels to stop. Instead,
- // command 4 includes channel 5 when initializing channel data, but
- // command 4 and 5 do not include channel 5 when initializing MIDI
- // channels and fading channels to stop. This is probably a bug, which is
- // not replicated in this reimplementation.
+ // Like the full game RSOUND.001, the demo RSOUND.001 driver includes
+ // channel 9 with the dynamic commands 4 and 5.
+ // Command 2 includes channel 5 when stopping playback, but command 2 and 3
+ // do not include channel 5 when initializing MIDI channels and fading
+ // channels to stop. Instead, command 4 includes channel 5 when stopping
+ // playback, but command 4 and 5 do not include channel 5 when initializing
+ // MIDI channels and fading channels to stop.
+ // This inconsistency is not replicated in this reimplementation.
+ // Interestingly, the demo RSOUND.001 does not include the playSoundDynamic
+ // function. All dynamically allocated sound data uses playSoundAnyChannel.
_dynamicStartChannel = 5;
- // The demo RSOUND.001 driver processes channel 9 with the dynamic
- // commands 4 and 5, instead of the static commands 2 and 3.
_dynamicIncludeChannel9 = true;
_staticIncludeChannel9 = false;
}
@@ -391,7 +400,7 @@ int RSoundDemo1::command13() {
}
int RSoundDemo1::command14() {
- playSoundAnyChannel(0x1AE2);
+ playSoundAnyChannel(0x1AE2, 0);
return 0;
}
@@ -463,7 +472,7 @@ int RSoundDemo1::patchAndPlaySound(int offset) {
byte *data = loadData(offset);
data[8] = (generateRandomNumber() & 0x18) + 0x2D;
data[5] = adjustedCommandParam() + 0x40;
- playSoundStatic(data, 8);
+ playSoundStatic(data, 8, 1);
return 0;
}
@@ -584,10 +593,22 @@ const RSound2::CommandPtr RSound2::_commandList[44] = {
RSound2::RSound2(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
RSound(mixer, midiDriver, "rsound.002", 0x1390, 0x42F0, 0x87, kRSoundFadeCheckAlternating) {
+ // RSOUND.002 is a bit of a mess when it comes to dynamic and static
+ // channels.
+ // playSoundDynamic uses channels 4-8. Commands 3 and 5 are consistent with
+ // this, as they fade out channels 1-3 and 9, and 4-8, respectively. These
+ // ranges have been reimplemented in this driver code.
+ // However, the original code commands 2 and 4 are not consistent with this
+ // and not consistent with itself. Playback is stopped on channels 1-5 and
+ // 6-9, while MIDI initialization is done on channels 1-4 and 5-9. These
+ // inconsistencies are not replicated in this reimplementation for now.
+ _dynamicStartChannel = 4;
+ _dynamicIncludeChannel9 = false;
+ _staticIncludeChannel9 = true;
}
int RSound2::command(int commandId, int param) {
- if (commandId > 43)
+ if (commandId < 0 || commandId > 43)
return 0;
_commandParam = param;
@@ -595,6 +616,12 @@ int RSound2::command(int commandId, int param) {
return (this->*_commandList[commandId])();
}
+int RSound2::command0() {
+ _volumeCycleCounter = 47;
+
+ return RSound::command0();
+}
+
int RSound2::command5() {
_volumeCycleCounter = 47;
for (int i = 4; i <= 8; i++) {
@@ -658,9 +685,9 @@ int RSound2::command15() {
byte *pData = loadData(0x1DFC);
if (!isSoundPlaying(pData)) {
command1();
- playSoundAnyChannel(0x1DFC);
- playSoundAnyChannel(0x222E);
- playSoundAnyChannel(0x2648);
+ playSoundAnyChannel(0x1DFC, 0);
+ playSoundAnyChannel(0x222E, 0);
+ playSoundAnyChannel(0x2648, 0);
playSoundStatic(0x26A2, 9);
}
return 0;
@@ -670,12 +697,12 @@ int RSound2::command16() {
byte *pData = loadData(0x3456);
if (!isSoundPlaying(pData)) {
command1();
- playSoundAnyChannel(0x3456);
- playSoundAnyChannel(0x3572);
- playSoundAnyChannel(0x367E);
- playSoundAnyChannel(0x37C6);
- playSoundAnyChannel(0x39AE);
- playSoundAnyChannel(0x3A3A);
+ playSoundAnyChannel(0x3456, 0);
+ playSoundAnyChannel(0x3572, 0);
+ playSoundAnyChannel(0x367E, 0);
+ playSoundAnyChannel(0x37C6, 0);
+ playSoundAnyChannel(0x39AE, 0);
+ playSoundAnyChannel(0x3A3A, 0);
}
return 0;
}
@@ -683,10 +710,10 @@ int RSound2::command16() {
int RSound2::command17() {
byte *pData = loadData(0x3AC0);
if (!isSoundPlaying(pData)) {
- playSoundDynamic(0x3AC0);
- playSoundDynamic(0x3C70);
- playSoundDynamic(0x3E16);
- playSoundDynamic(0x3FBE);
+ playSoundDynamic(0x3AC0, 0);
+ playSoundDynamic(0x3C70, 0);
+ playSoundDynamic(0x3E16, 0);
+ playSoundDynamic(0x3FBE, 0);
}
return 0;
}
@@ -704,12 +731,12 @@ int RSound2::command19() {
byte *pData = loadData(0x2A64);
if (!isSoundPlaying(pData)) {
command1();
- playSoundAnyChannel(0x2A64);
- playSoundAnyChannel(0x2BE2);
- playSoundAnyChannel(0x2DAC);
- playSoundAnyChannel(0x2ECE);
- playSoundAnyChannel(0x3026);
- playSoundAnyChannel(0x30C2);
+ playSoundAnyChannel(0x2A64, 0);
+ playSoundAnyChannel(0x2BE2, 0);
+ playSoundAnyChannel(0x2DAC, 0);
+ playSoundAnyChannel(0x2ECE, 0);
+ playSoundAnyChannel(0x3026, 0);
+ playSoundAnyChannel(0x30C2, 0);
}
return 0;
}
@@ -819,8 +846,8 @@ int RSound2::command35() {
}
int RSound2::command36() {
- playSoundDynamic(0xFF4);
- playSoundDynamic(0x1008);
+ playSoundDynamic(0xFF4, 0);
+ playSoundDynamic(0x1008, 0);
return 0;
}
@@ -833,12 +860,12 @@ int RSound2::command38() {
byte *pData = loadData(0x2B0E);
if (!isSoundPlaying(pData)) {
command1();
- playSoundAnyChannel(0x2B0E);
- playSoundAnyChannel(0x2CD0);
- playSoundAnyChannel(0x2E46);
- playSoundAnyChannel(0x2F7C);
- playSoundAnyChannel(0x3074);
- playSoundAnyChannel(0x317E);
+ playSoundAnyChannel(0x2B0E, 0);
+ playSoundAnyChannel(0x2CD0, 0);
+ playSoundAnyChannel(0x2E46, 0);
+ playSoundAnyChannel(0x2F7C, 0);
+ playSoundAnyChannel(0x3074, 0);
+ playSoundAnyChannel(0x317E, 0);
}
return 0;
}
@@ -895,7 +922,7 @@ RSound3::RSound3(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
}
int RSound3::command(int commandId, int param) {
- if (commandId > 60)
+ if (commandId < 0 || commandId > 60)
return 0;
_commandParam = param;
@@ -909,6 +936,12 @@ Channel *RSound3::patchAndPlaySound(int offset, byte value) {
return playSoundDynamic(offset);
}
+int RSound3::command5() {
+ if (!isSoundPlaying(0x1AE6))
+ return RSound::command5();
+ return 0;
+}
+
int RSound3::command9() {
command1();
setFadeOutSpeed((byte)_commandParam);
@@ -1149,8 +1182,8 @@ int RSound3::command35() {
}
int RSound3::command36() {
- playSoundDynamic(0x14DA);
- playSoundDynamic(0x14EE);
+ playSoundDynamic(0x14DA, 0);
+ playSoundDynamic(0x14EE, 0);
return 0;
}
@@ -1271,7 +1304,7 @@ RSound4::RSound4(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
}
int RSound4::command(int commandId, int param) {
- if (commandId > 59)
+ if (commandId < 0 || commandId > 59)
return 0;
_commandParam = param;
@@ -1292,6 +1325,14 @@ void RSound4::stopChannel(byte channel) {
getChannel(channel)->_pSrc = loadData(0x1188);
}
+int RSound4::command0() {
+ _callbackCounter = 0;
+ _callbackPeriod = 0;
+ _callbackFnPtr = nullptr;
+
+ return RSound::command0();
+}
+
int RSound4::command9() {
command1();
setFadeOutSpeed((byte)_commandParam);
@@ -1385,8 +1426,8 @@ int RSound4::command35() {
}
int RSound4::command36() {
- playSoundDynamic(0x123C);
- playSoundDynamic(0x1250);
+ playSoundDynamic(0x123C, 0);
+ playSoundDynamic(0x1250, 0);
return 0;
}
@@ -1407,15 +1448,15 @@ int RSound4::command53() {
command1();
_callbackCounter = 56;
_callbackPeriod = 56;
- playSoundAnyChannel(0x1888);
- playSoundAnyChannel(0x18DE);
+ playSoundAnyChannel(0x1888, 0);
+ playSoundAnyChannel(0x18DE, 0);
return 0;
}
void RSound4::command54Callback() {
_callbackFnPtr = nullptr;
- playSoundAnyChannel(0x18B2);
- playSoundAnyChannel(0x1904);
+ playSoundAnyChannel(0x18B2, 0);
+ playSoundAnyChannel(0x1904, 0);
}
int RSound4::command54() {
@@ -1426,7 +1467,7 @@ int RSound4::command54() {
void RSound4::command55Callback() {
_callbackFnPtr = nullptr;
- playSoundAnyChannel(0x191E);
+ playSoundAnyChannel(0x191E, 0);
}
int RSound4::command55() {
@@ -1437,7 +1478,7 @@ int RSound4::command55() {
void RSound4::command56Callback() {
_callbackFnPtr = nullptr;
- playSoundAnyChannel(0x185C);
+ playSoundAnyChannel(0x185C, 0);
}
int RSound4::command56() {
@@ -1483,7 +1524,7 @@ RSound5::RSound5(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
}
int RSound5::command(int commandId, int param) {
- if (commandId > 41)
+ if (commandId < 0 || commandId > 41)
return 0;
_commandParam = param;
@@ -1522,7 +1563,7 @@ int RSound5::command13() {
}
int RSound5::command14() {
- playSoundStatic(0x1272, 8);
+ playSoundStatic(0x1272, 8, 1);
return 0;
}
@@ -1650,8 +1691,8 @@ int RSound5::command35() {
}
int RSound5::command36() {
- playSoundDynamic(0x1464);
- playSoundDynamic(0x1478);
+ playSoundDynamic(0x1464, 0);
+ playSoundDynamic(0x1478, 0);
return 0;
}
@@ -1701,7 +1742,7 @@ RSound6::RSound6(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
}
int RSound6::command(int commandId, int param) {
- if (commandId > 29)
+ if (commandId < 0 || commandId > 29)
return 0;
_commandParam = param;
@@ -1718,6 +1759,14 @@ void RSound6::tickCallback() {
(this->*_callbackFnPtr)();
}
+int RSound6::command0() {
+ _callbackCounter = 0;
+ _callbackPeriod = 0;
+ _callbackFnPtr = nullptr;
+
+ return RSound::command0();
+}
+
int RSound6::command9() {
playSoundDynamic(0x111C);
return 0;
@@ -1783,7 +1832,7 @@ int RSound6::command20() {
}
int RSound6::command21() {
- playSoundStatic(0x1282, 5);
+ playSoundStatic(0x1282, 5, 1);
playSoundDynamic(0x1282);
playSoundDynamic(0x1282);
playSoundDynamic(0x12AA);
@@ -1791,10 +1840,10 @@ int RSound6::command21() {
}
int RSound6::command22() {
- playSoundStatic(0x12CE, 5);
- playSoundStatic(0x12CE, 6);
- playSoundStatic(0x12CE, 7);
- playSoundStatic(0x12CE, 8);
+ playSoundStatic(0x12CE, 5, 1);
+ playSoundStatic(0x12CE, 6, 1);
+ playSoundStatic(0x12CE, 7, 1);
+ playSoundStatic(0x12CE, 8, 1);
return 0;
}
@@ -1826,7 +1875,7 @@ int RSound6::command24() {
}
int RSound6::command25() {
- playSoundStatic(0x12FE, 5);
+ playSoundStatic(0x12FE, 5, 1);
return 0;
}
@@ -1877,7 +1926,7 @@ RSound7::RSound7(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
}
int RSound7::command(int commandId, int param) {
- if (commandId > 37)
+ if (commandId < 0 || commandId > 37)
return 0;
_commandParam = param;
@@ -1910,7 +1959,7 @@ int RSound7::command17() {
}
int RSound7::command18() {
- playSoundStatic(0x12FC, 8);
+ playSoundStatic(0x12FC, 8, 1);
return 0;
}
@@ -2000,8 +2049,8 @@ int RSound7::command35() {
}
int RSound7::command36() {
- playSoundDynamic(0x1358);
- playSoundDynamic(0x136C);
+ playSoundDynamic(0x1358, 0);
+ playSoundDynamic(0x136C, 0);
return 0;
}
@@ -2030,7 +2079,7 @@ RSound8::RSound8(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
}
int RSound8::command(int commandId, int param) {
- if (commandId > 37)
+ if (commandId < 0 || commandId > 37)
return 0;
_commandParam = param;
@@ -2044,10 +2093,10 @@ int RSound8::command9() {
}
int RSound8::command10() {
- playSoundStatic(0x115A, 1);
- playSoundStatic(0x115A, 2);
- playSoundStatic(0x115A, 3);
- playSoundStatic(0x1150, 4);
+ playSoundStatic(0x115A, 1, 1);
+ playSoundStatic(0x115A, 2, 1);
+ playSoundStatic(0x115A, 3, 1);
+ playSoundStatic(0x1150, 4, 1);
return 0;
}
@@ -2127,10 +2176,10 @@ int RSound8::command22() {
}
int RSound8::command23() {
- playSoundStatic(0x128E, 1);
- playSoundStatic(0x128E, 2);
- playSoundStatic(0x128E, 3);
- playSoundStatic(0x128E, 4);
+ playSoundStatic(0x128E, 1, 1);
+ playSoundStatic(0x128E, 2, 1);
+ playSoundStatic(0x128E, 3, 1);
+ playSoundStatic(0x128E, 4, 1);
return 0;
}
@@ -2207,8 +2256,8 @@ int RSound8::command35() {
}
int RSound8::command36() {
- playSoundDynamic(0x12E6);
- playSoundDynamic(0x12FA);
+ playSoundDynamic(0x12E6, 0);
+ playSoundDynamic(0x12FA, 0);
return 0;
}
@@ -2237,13 +2286,19 @@ const RSound9::CommandPtr RSound9::_commandList[52] = {
RSound9::RSound9(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
RSound(mixer, midiDriver, "rsound.009", 0x1520, 0x8920, 0x6F, kRSoundFadeCheckAlternating) {
+ // The full game RSOUND.009 driver uses static channels 1-5 and dynamic
+ // channels 6-9.
+ _dynamicStartChannel = 6;
+ _dynamicIncludeChannel9 = true;
+ _staticIncludeChannel9 = false;
+
_callbackCounter = 0;
_callbackPeriod = 0;
_callbackFnPtr = nullptr;
}
int RSound9::command(int commandId, int param) {
- if (commandId > 51)
+ if (commandId < 0 || commandId > 51)
return 0;
_commandParam = param;
@@ -2264,6 +2319,7 @@ int RSound9::command0() {
_callbackCounter = 0;
_callbackPeriod = 0;
_callbackFnPtr = nullptr;
+
return RSound::command0();
}
@@ -2289,37 +2345,37 @@ int RSound9::command10() {
}
int RSound9::command11() {
- playSoundStatic(0x33E2, 8);
+ playSoundStatic(0x33E2, 8, 1);
return 0;
}
int RSound9::command12() {
- playSoundStatic(0x342E, 8);
+ playSoundStatic(0x342E, 8, 1);
return 0;
}
int RSound9::command13() {
- playSoundStatic(0x343A, 8);
+ playSoundStatic(0x343A, 8, 1);
return 0;
}
int RSound9::command14() {
- playSoundStatic(0x3442, 8);
+ playSoundStatic(0x3442, 8, 1);
return 0;
}
int RSound9::command15() {
- playSoundStatic(0x3462, 8);
+ playSoundStatic(0x3462, 8, 1);
return 0;
}
int RSound9::command16() {
- playSoundStatic(0x347A, 8);
+ playSoundStatic(0x347A, 8, 1);
return 0;
}
int RSound9::command17() {
- playSoundStatic(0x3470, 8);
+ playSoundStatic(0x3470, 8, 1);
return 0;
}
@@ -2396,7 +2452,7 @@ int RSound9::command28() {
int v = (generateRandomNumber() & 28) + 15;
byte *pData = loadData(0x334A);
pData[6] = v & 0x7F;
- playSoundStatic(pData, 8);
+ playSoundStatic(pData, 8, 1);
return 0;
}
@@ -2668,9 +2724,8 @@ const RSoundDemo9::CommandPtr RSoundDemo9::_commandList[40] = {
RSoundDemo9::RSoundDemo9(Audio::Mixer *mixer, MidiDriver_MT32GM *midiDriver) :
RSound(mixer, midiDriver, "rsound.009", 0x11D0, 0x3664, 0x69, kRSoundFadeCheckAlternating) {
- // The demo RSOUND.009 driver uses channel range 1-5 for the static init
- // and stop commands 2 and 3, and 6-9 for the dynamic init and stop
- // commands 4 and 5.
+ // Like the full game, the demo RSOUND.009 driver uses static channels 1-5
+ // and dynamic channels 6-9.
_dynamicStartChannel = 6;
_dynamicIncludeChannel9 = true;
_staticIncludeChannel9 = false;
@@ -2686,37 +2741,37 @@ int RSoundDemo9::command(int commandId, int param) {
}
int RSoundDemo9::command11() {
- playSoundStatic(0x1454, 8);
+ playSoundStatic(0x1454, 8, 1);
return 0;
}
int RSoundDemo9::command12() {
- playSoundStatic(0x14A0, 8);
+ playSoundStatic(0x14A0, 8, 1);
return 0;
}
int RSoundDemo9::command13() {
- playSoundStatic(0x14AC, 8);
+ playSoundStatic(0x14AC, 8, 1);
return 0;
}
int RSoundDemo9::command14() {
- playSoundStatic(0x14B4, 8);
+ playSoundStatic(0x14B4, 8, 1);
return 0;
}
int RSoundDemo9::command15() {
- playSoundStatic(0x14D4, 8);
+ playSoundStatic(0x14D4, 8, 1);
return 0;
}
int RSoundDemo9::command16() {
- playSoundStatic(0x14EC, 8);
+ playSoundStatic(0x14EC, 8, 1);
return 0;
}
int RSoundDemo9::command17() {
- playSoundStatic(0x14E2, 8);
+ playSoundStatic(0x14E2, 8, 1);
return 0;
}
diff --git a/engines/mads/nebular/sound/rsound_nebular.h b/engines/mads/nebular/sound/rsound_nebular.h
index 2e5f2c40a94..e4c21a06f6f 100644
--- a/engines/mads/nebular/sound/rsound_nebular.h
+++ b/engines/mads/nebular/sound/rsound_nebular.h
@@ -159,7 +159,8 @@ private:
*/
byte _volumeCycleCounter = 0x2F;
- int command5();
+ int command0() override;
+ int command5() override;
int command9();
int command10();
@@ -253,6 +254,8 @@ private:
*/
void sendDualVolume(byte volume);
+ int command5() override;
+
int command9();
int command10();
int command11();
@@ -344,6 +347,8 @@ private:
*/
void stopChannel(byte channel);
+ int command0() override;
+
int command9();
int command10();
int command12();
@@ -455,6 +460,8 @@ private:
void command24Callback();
void command29Callback();
+ int command0() override;
+
int command9();
int command10();
int command11();
Commit: 03e90922c932bc5ed41eb0e95a9baf6b1e7c8bfa
https://github.com/scummvm/scummvm/commit/03e90922c932bc5ed41eb0e95a9baf6b1e7c8bfa
Author: Coen Rampen (crampen at gmail.com)
Date: 2026-10-09T18:19:49+02:00
Commit Message:
MADS: Fix Rex MT-32 volume fade-out bug
Changed paths:
engines/mads/nebular/sound/rsound.cpp
diff --git a/engines/mads/nebular/sound/rsound.cpp b/engines/mads/nebular/sound/rsound.cpp
index 204e74128c0..c2761f1d2be 100644
--- a/engines/mads/nebular/sound/rsound.cpp
+++ b/engines/mads/nebular/sound/rsound.cpp
@@ -31,6 +31,7 @@ namespace Sound {
void Channel::loadData(byte *soundData, byte source) {
_pitchSlideStepSize = 0;
+ _volumeFadeStepSize = 0;
_panningSweepStepSize = 0;
_innerLoopCounter = 0;
_outerLoopCounter = 0;
@@ -713,7 +714,7 @@ void RSound::Channel_processTick(Channel *channel) {
// Process effects
// Volume fade
- if (channel->_volumeFadeStepSize > 0 && --channel->_volumeFadeCounter == 0) {
+ if (channel->_volumeFadeStepSize != 0 && --channel->_volumeFadeCounter == 0) {
channel->_volumeFadeCounter = channel->_volumeFadeSpeed;
int newVolume = channel->_volume + channel->_volumeFadeStepSize;
if (newVolume < 0 || newVolume > 0x7F) {
More information about the Scummvm-git-logs
mailing list