ScummVM API documentation
MADS::Phantom::Sound::RSound Class Referenceabstract

#include <rsound.h>

Inheritance diagram for MADS::Phantom::Sound::RSound:
MADS::SoundDriver MADS::Phantom::Sound::RSound1 MADS::Phantom::Sound::RSound2 MADS::Phantom::Sound::RSound3 MADS::Phantom::Sound::RSound4 MADS::Phantom::Sound::RSound5 MADS::Phantom::Sound::RSound9 MADS::Phantom::Sound::RSoundDemo MADS::Phantom::Sound::RSoundDemoPHA

Public Member Functions

 RSound (Audio::Mixer *mixer, const Common::Path &filename, int dataOffset, int dataSize, int sysExOffset)
 
int stop () override
 
int poll () override
 
void noise () override
 
void setVolume (int volume) override
 
int getFrameCounter ()
 
- Public Member Functions inherited from MADS::SoundDriver
 SoundDriver (Audio::Mixer *mixer, const Common::Path &filename, int dataOffset, int dataSize)
 
virtual int command (int commandId, int param)=0
 

Static Public Member Functions

static void validate ()
 

Public Attributes

Channel _channels [9]
 
int _frameCounter
 
bool _isDisabled
 
int _pollResult
 

Protected Member Functions

byte * loadData (int offset)
 
void resetChannelRange (int first, int last)
 
virtual void checkRandomAmbianceTrigger ()
 
void resultCheck ()
 
virtual bool callFunction (uint16 targetOffset)
 
Channel * playSound (int offset)
 
Channel * playSoundChannels1To5 (int offset)
 
Channel * playSoundAny (int offset)
 
Channel * playSoundChannels5To8 (int offset)
 
Channel * playSoundChannels1To6 (int offset)
 
Channel * playSoundData (byte *pData, int startingChannel, int freeScanEnd, int fallbackScanEnd)
 
bool isSoundActive (byte *pData)
 
int getRandomNumber ()
 
void sendNoteOn (int midiChannel, int note, int velocity)
 
void sendProgramChange (int midiChannel, int program)
 
void sendVolume (int midiChannel, int volume)
 
void sendVolumeCC (int midiChannel, int volume)
 
void resetPitchBend (int midiChannel)
 
void sendPitchBend (int midiChannel, int value)
 
void sendPan (int midiChannel, int value)
 
void muteChannel (int midiChannel)
 
void sendGmReset (int count)
 
const byte * sendSysExData (const byte *pData, uint maxLength)
 
const byte * sendSysEx (int offset)
 
void sendSysExSequence ()
 
void restorePatchMemory ()
 
void sendReverbSysEx (int mode, int time, int level)
 
void onTimer ()
 
void noOpHandler (int param)
 
virtual int command0 ()
 
int command1 ()
 
int command2 ()
 
int command3 ()
 
void enableUpperChannels ()
 
void resetAndGmResetUpperChannels ()
 
virtual int command4 ()=0
 
virtual int command5 ()=0
 
int command6 ()
 
int command7 ()
 
int command8 ()
 
int nullCommand ()
 
- Protected Member Functions inherited from MADS::SoundDriver
Common::MemoryReadStream getDataStream (int offset) const
 
 SoundDriver (Audio::Mixer *mixer)
 

Static Protected Member Functions

static void timerCallback (void *data)
 

Protected Attributes

int _commandParam
 
int _randomAmbianceTriggerFlag
 
int _fadeCheckPeriod
 
- Protected Attributes inherited from MADS::SoundDriver
Audio::Mixer * _mixer
 
Common::Array< byte > _soundData
 
Common::Mutex _driverMutex
 

Detailed Description

Base class for the Return of the Phantom MT-32 / MPU-401 sound player resource files (rsound.ph1-.ph9). Mirrors the structure of the Rex Nebular RSound family (see nebular/rsound.h), adapted for this game's substantially richer Channel_pollActive script VM (~65 opcodes here vs. ~15 in Rex Nebular): general arithmetic on a script-variable table, conditional branches, and a call/return pair, in addition to the shared note/fade/loop mechanics.

NOTE: DOS-specific driver ceremony from the original (timer IRQ hooking, MPU-401 hardware detection/reset, PIT-based SysEx delay calibration, the system-clock save/restore around it) has no ScummVM equivalent and is not ported - matching how Rex Nebular's RSound skips the same kind of hardware-detection dance.

Constructor & Destructor Documentation

◆ RSound()

MADS::Phantom::Sound::RSound::RSound ( Audio::Mixer *  mixer,
const Common::Path &  filename,
int  dataOffset,
int  dataSize,
int  sysExOffset 
)

Constructor

Parameters
mixerMixer
filenameSpecifies the Roland sound player file to use
dataOffsetOffset in the file of the data segment
dataSizeSize of the data segment
sysExOffsetOffset of this driver's own command0_array

Member Function Documentation

◆ resetChannelRange()

void MADS::Phantom::Sound::RSound::resetChannelRange ( int  first,
int  last 
)
protected

Zeroes _activeCount/_pitchBendFadeStep/_volumeFadeStep/_panFadeStep for channels [first, last] (0-based indices). Protected (not private) so per-driver command4()/command5() overrides that need a narrower or differently-shaped reset than resetChannels4to9() / resetAndGmResetUpperChannels() below can call it directly - e.g. RSound3's command4(), confirmed to reset only channels 5-9 (indices 4-8), not 4-9.

◆ checkRandomAmbianceTrigger()

virtual void MADS::Phantom::Sound::RSound::checkRandomAmbianceTrigger ( )
inlineprotectedvirtual

Hook called once per update() frame after the disabled check. Only drivers with a random-ambiance/music picker (e.g. RSound1's command16) override this; every other driver leaves it a no-op. Confirmed shape: if _randomAmbianceTriggerFlag == 0xFF, clear it and fire the driver-specific picker.

◆ callFunction()

virtual bool MADS::Phantom::Sound::RSound::callFunction ( uint16  targetOffset)
inlineprotectedvirtual

Handles a native C4 callback target embedded in a sequence. Controllers override this only for statically identified native targets; the default remains a fatal rejection in the bytecode interpreter.

◆ playSound()

Channel* MADS::Phantom::Sound::RSound::playSound ( int  offset)
protected

Plays the specified sound, using any free channel from 6 to 8. Matches the disassembly's playSound exactly (rsound_channel6-8).

◆ playSoundChannels1To5()

Channel* MADS::Phantom::Sound::RSound::playSoundChannels1To5 ( int  offset)
protected

Plays the specified sound, using any free channel from 1 to 5. NOTE: unlike Rex Nebular's playSoundAny() (which reaches all 9 channels), THIS driver's playSoundAny only scans channels 1-5 for a free slot - confirmed directly from the disassembly. Also confirmed: the pending-stop fallback scan only reaches down to channel 4, NOT channel 5 - a genuine asymmetry preserved exactly (channel 5 can never be pre-empted by this call, only picked while free).

◆ playSoundAny()

Channel* MADS::Phantom::Sound::RSound::playSoundAny ( int  offset)
protected

Plays the specified sound, using any free channel from 1 to 8 (everything except channel 9) - a third, distinct scan range from playSound() and playSoundAny() above.

◆ playSoundChannels5To8()

Channel* MADS::Phantom::Sound::RSound::playSoundChannels5To8 ( int  offset)
protected

Plays the specified sound, using any free channel from 5 to 8. Matches playChannels5to8 in the disassembly - a fourth, distinct scan range (symmetric free/fallback scan, unlike playSoundAny()'s asymmetry, and one channel narrower than playSoundAny()'s 1-8).

◆ playSoundChannels1To6()

Channel* MADS::Phantom::Sound::RSound::playSoundChannels1To6 ( int  offset)
protected

Plays the specified sound, using any free channel from 1 to 6. Matches playSoundChannels1to6 in the disassembly - a fifth, distinct scan range (symmetric free/fallback scan).

◆ playSoundData()

Channel* MADS::Phantom::Sound::RSound::playSoundData ( byte *  pData,
int  startingChannel,
int  freeScanEnd,
int  fallbackScanEnd 
)
protected

Scans [startingChannel, freeScanEnd] for a free channel; if none found, scans [startingChannel, fallbackScanEnd] in reverse for a pending-stop channel to pre-empt. The two end bounds are usually the same, but playSoundAny() is a confirmed exception (see above).

◆ isSoundActive()

bool MADS::Phantom::Sound::RSound::isSoundActive ( byte *  pData)
protected

Checks whether the given block of data is already loaded into a channel.

◆ sendGmReset()

void MADS::Phantom::Sound::RSound::sendGmReset ( int  count)
protected

Sends the GM-reset Control Change sequence (all notes off, reset all controllers, volume=100, pan=center) to count MIDI channels, counting down from count to 1.

◆ sendSysExData()

const byte* MADS::Phantom::Sound::RSound::sendSysExData ( const byte *  pData,
uint  maxLength 
)
protected

Sends a single Roland DT1-style SysEx message from a raw buffer: the fixed header, then bytes from pData up to (not including) a 0xFF terminator - each byte sent and folded into a running checksum - then the checksum byte and a closing F7. The shared core of sendSysEx()/sendReverbSysEx() below - split out so hardcoded protocol buffers (not driver-specific loaded sound data) can be sent without going through loadData(). Returns a pointer to the terminating 0xFF byte (matching the disassembly's own si register value on return), so callers walking a sequence of consecutive messages can advance past it to find the next one. Returns nullptr when no terminator occurs within maxLength bytes.

◆ sendSysEx()

const byte* MADS::Phantom::Sound::RSound::sendSysEx ( int  offset)
protected

sendSysExData() for a block already in this driver's own loaded sound data.

◆ sendSysExSequence()

void MADS::Phantom::Sound::RSound::sendSysExSequence ( )
protected

Matches sendSysExSequence: repeatedly calls sendSysEx(), starting from this driver's own command0_array (_sysExOffset) and advancing past each message's terminating 0xFF to the start of the next one, until an empty message (two consecutive 0xFF bytes) marks the end of the table. Called once from the constructor (matching initDeviceOnce) - the disassembly's _deviceInitialized guard flag isn't needed since nothing else ever calls this again.

◆ restorePatchMemory()

void MADS::Phantom::Sound::RSound::restorePatchMemory ( )
protected

Restore all 128 MT-32 Patch Memory records to the standard A/B timbre mapping. Native export 2 calls this during driver teardown, after stopping playback. All checked Phantom retail and demo overlays use the same four-block, 32-record structure.

ScummVM does not call the DOS hardware teardown path: opening each MidiDriver_MT32GM resets the selected device, while closing follows the shared MIDI-driver lifecycle. Keep the exact native translation available without imposing its synchronous teardown sequence.

◆ sendReverbSysEx()

void MADS::Phantom::Sound::RSound::sendReverbSysEx ( int  mode,
int  time,
int  level 
)
protected

CONFIRMED: masks the 3 caller-supplied values to 2/3/3 bits (mode 0-3, time 0-7, level 0-7) and sends them via the real Roland MT-32 System Area Reverb SysEx address (10 00 01h), hardcoded here rather than read via loadData(), since it's a fixed hardware protocol address, not driver-specific sound data.

◆ noOpHandler()

void MADS::Phantom::Sound::RSound::noOpHandler ( int  param)
inlineprotected

A confirmed no-op (reads one operand, does nothing with it).

◆ enableUpperChannels()

void MADS::Phantom::Sound::RSound::enableUpperChannels ( )
protected

Shared tail of command1() (falls through into it after command3()) and command5() (jumps straight into it, ungated, in every driver confirmed so far): enables channels 5,6,7,8.

◆ resetAndGmResetUpperChannels()

void MADS::Phantom::Sound::RSound::resetAndGmResetUpperChannels ( )
protected

Shared tail of command4() in every driver confirmed so far: resetChannels4to9() + sendGmReset(9).

◆ command4()

virtual int MADS::Phantom::Sound::RSound::command4 ( )
protectedpure virtual

PURE VIRTUAL, unlike command1-3/6-8. Confirmed (from RSound1 AND RSound2, independently) that every driver's command4/command5 are gated by isSoundActive() on a driver-specific data offset before calling resetAndGmResetUpperChannels()/enableUpperChannels() above

  • a per-driver detail that must not live in the shared base. Deliberately NO default implementation, so a new driver subclass can't compile without explicitly providing its own gate - silently falling back to an ungated version would be wrong (and was, in an earlier version of this port, until RSound1/RSound2 confirmed the gate is universal even though its offset isn't).

Implemented in MADS::Phantom::Sound::RSoundDemo.

◆ validate()

static void MADS::Phantom::Sound::RSound::validate ( )
static

Validates the presence of the sound driver files.

◆ stop()

int MADS::Phantom::Sound::RSound::stop ( )
overridevirtual

Stop all currently playing sounds

Implements MADS::SoundDriver.

◆ poll()

int MADS::Phantom::Sound::RSound::poll ( )
overridevirtual

Main poll method to allow sounds to progress

Implements MADS::SoundDriver.

◆ noise()

void MADS::Phantom::Sound::RSound::noise ( )
inlineoverridevirtual

General noise/note output

Implements MADS::SoundDriver.

◆ setVolume()

void MADS::Phantom::Sound::RSound::setVolume ( int  volume)
overridevirtual

Set the volume

Implements MADS::SoundDriver.

Member Data Documentation

◆ _randomAmbianceTriggerFlag

int MADS::Phantom::Sound::RSound::_randomAmbianceTriggerFlag
protected

Cleared to 0 by RSound1's command37 (a "cancel any pending random-ambiance trigger" side effect of playing that specific sound). CONFIRMED: the only code that ever sets it to 0xFF (arming checkRandomAmbianceTrigger()) is itself unreachable/dead code - so in the real game this mechanism never actually fires. Implemented faithfully anyway in case that changes for a different driver. Protected (not private) so driver subclasses with their own commands touching it (like RSound1's command37) can reach it directly.

◆ _fadeCheckPeriod

int MADS::Phantom::Sound::RSound::_fadeCheckPeriod
protected

Half-rate fade-check period reload value (see _fadeCheckCounter above). Protected (not private) so per-driver command1()/command3()/ command5() overrides that need to arm it directly (matching an inline "mov cs:_fadeCheckPeriod, 1" in the disassembly, e.g. RSound5's driver-specific 6-channel command3() and 3-channel enableChannels678()) can do so without going through the base class's own command3()/enableUpperChannels().


The documentation for this class was generated from the following file: