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

#include <rsound.h>

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

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
 

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 ()
 
ChannelplaySound (int offset)
 
ChannelplaySoundChannels1To5 (int offset)
 
ChannelplaySoundAny (int offset)
 
ChannelplaySoundChannels5To8 (int offset)
 
ChannelplaySoundChannels1To6 (int offset)
 
ChannelplaySoundData (byte *pData, int startingChannel, int freeScanEnd, int fallbackScanEnd)
 
bool isSoundActive (byte *pData)
 
int getRandomNumber ()
 
void sendMidiByte (byte value)
 
void sendStatus (int midiChannel, byte statusNibble)
 
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)
 
void sendSysExData (const byte *pData)
 
void sendSysEx (int offset)
 
void sendPatchInitSequence ()
 
void sendReverbSysEx (int mode, int time, int level)
 
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
 

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: The actual MIDI transmission (sendMidiByte()) currently just logs via warning() - it isn't hooked up to a real ScummVM MIDI/MT-32 output yet, matching the Rex Nebular RSound family. Every other MIDI-sending helper funnels through sendMidiByte().

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::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::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::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. Matches sub_1222E's confirmed shape: if _randomAmbianceTriggerFlag == 0xFF, clear it and fire the driver-specific picker.

◆ playSound()

Channel* MADS::Phantom::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::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::RSound::playSoundAny ( int  offset)
protected

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

◆ playSoundChannels5To8()

Channel* MADS::Phantom::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::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::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::RSound::isSoundActive ( byte *  pData)
protected

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

◆ sendGmReset()

void MADS::Phantom::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. Matches sub_1068A.

◆ sendSysExData()

void MADS::Phantom::RSound::sendSysExData ( const byte *  pData)
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().

◆ sendSysEx()

void MADS::Phantom::RSound::sendSysEx ( int  offset)
protected

sendSysExData() for a block already in this driver's own loaded sound data. Matches sendSysEx exactly (same algorithm as the confirmed Rex Nebular RSound::sendSysEx()).

◆ sendPatchInitSequence()

void MADS::Phantom::RSound::sendPatchInitSequence ( )
protected

TENTATIVE: matches sub_102BE - a nested loop (4 outer x 32 inner iterations) building and sending a SysEx message each inner pass. The overall shape (loop counters, accumulating base value, fixed bytes 0x18/0x32/0x0C) is clear from the disassembly, but the exact purpose (a bulk patch/rhythm-setup initialization sequence is the working hypothesis) is not confirmed.

◆ sendReverbSysEx()

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

CONFIRMED: matches sub_108C7 - 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 - originally found at RSound1's dseg offset 0xA9, but hardcoded here rather than read via loadData(), since it's a fixed hardware protocol address, not driver-specific sound data).

◆ noOpHandler()

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

Matches sub_108F9 - a confirmed no-op (reads one operand, does nothing with it).

◆ enableUpperChannels()

void MADS::Phantom::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. Matches loc_108A9.

◆ resetAndGmResetUpperChannels()

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

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

◆ command4()

virtual int MADS::Phantom::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).

◆ stop()

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

Stop all currently playing sounds

Implements MADS::SoundDriver.

◆ poll()

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

Main poll method to allow sounds to progress

Implements MADS::SoundDriver.

◆ noise()

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

General noise/note output

Implements MADS::SoundDriver.

◆ setVolume()

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

Set the volume

Implements MADS::SoundDriver.

Member Data Documentation

◆ _randomAmbianceTriggerFlag

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

byte_1303E in the disassembly. 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 (matching sub_1222E's shape exactly) 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::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: