diff options
author | Paul Gilbert | 2016-09-14 20:53:35 -0400 |
---|---|---|
committer | Paul Gilbert | 2016-09-14 20:53:35 -0400 |
commit | b93b8f8245400c937f0d571f9a076d552b6fd0ce (patch) | |
tree | cdedf865ebbdaa02755615ac7bbc1a128b55ac07 | |
parent | 87168baf323c9db3acd90d154848fd4f3a554df6 (diff) | |
download | scummvm-rg350-b93b8f8245400c937f0d571f9a076d552b6fd0ce.tar.gz scummvm-rg350-b93b8f8245400c937f0d571f9a076d552b6fd0ce.tar.bz2 scummvm-rg350-b93b8f8245400c937f0d571f9a076d552b6fd0ce.zip |
XEEN: Fleshing out music player
-rw-r--r-- | engines/xeen/music.cpp | 551 | ||||
-rw-r--r-- | engines/xeen/music.h | 261 |
2 files changed, 562 insertions, 250 deletions
diff --git a/engines/xeen/music.cpp b/engines/xeen/music.cpp index 72ff3866cf..932cca1ccb 100644 --- a/engines/xeen/music.cpp +++ b/engines/xeen/music.cpp @@ -27,277 +27,492 @@ namespace Xeen { -#define CALLBACKS_PER_SECOND 72 +#define CALLBACKS_PER_SECOND 73 -Music::Music(Audio::Mixer *mixer) : _mixer(mixer), _effectsData(nullptr), - _musicPtr1(nullptr), _musicPtr2(nullptr), _dataPtr(nullptr), - _lowMusicIgnored(false), - _fieldF(false), _field1C(false), _field1E(false), _field109(0), - _field10B(0), _field114(0), _field115(0), _field116(0), _field117(0) { - _channels.resize(ADLIB_CHANNEL_COUNT); - Common::fill(&_fieldFB[0], &_fieldFB[7], 0); - Common::fill(&_field10D[0], &_field10D[7], 0); +/*------------------------------------------------------------------------*/ - _mixer = mixer; - _opl = OPL::Config::create(); - _opl->init(); - _opl->start(new Common::Functor0Mem<void, Music>(this, &Music::onTimer), CALLBACKS_PER_SECOND); - initialize(); - - loadEffectsData(); +MusicDriver::MusicDriver() : _fieldF(false), _field1E(false), _lowMusicIgnored(false), + _musCountdownTimer(0), _fxCountdownTimer(0), _musDataPtr(nullptr), + _fxDataPtr(nullptr), _fxStartPtr(nullptr), _musStartPtr(nullptr) { + Common::fill(&_flags[0], &_flags[CHANNEL_COUNT], false); + Common::fill(&_field15C[0], &_field15C[CHANNEL_COUNT], 0); + Common::fill(&_field165[0], &_field165[CHANNEL_COUNT], 0); + Common::fill(&_field177[0], &_field177[CHANNEL_COUNT], 0); } -Music::~Music() { - _opl->stop(); - delete _opl; - delete[] _effectsData; +void MusicDriver::execute() { + bool isFX = false; + bool flag = !_field1E; + const byte *srcP = _musDataPtr; + + if (!flag) { + if (_musCountdownTimer && --_musCountdownTimer == 0) + flag = true; + } + if (flag && _lowMusicIgnored) { + srcP = _fxDataPtr; + isFX = true; + if (!_fxCountdownTimer || --_fxCountdownTimer == 0) + flag = false; + } + + if (flag) { + postProcess(); + return; + } + + // Main loop + bool breakFlag = false; + while (!breakFlag) { + byte nextByte = *srcP++; + int cmd = (nextByte >> 4) & 15; + int param = (nextByte & 15); + + CommandFn fn = isFX ? FX_COMMANDS[cmd] : MUSIC_COMMANDS[cmd]; + breakFlag = (this->*fn)(srcP, param); + } } -void Music::initialize() { - write(1, 0x20); - write(8, 0); - write(0xBD, 0); - resetFrequencies(); - reset(); +bool MusicDriver::musCallSubroutine(const byte *&srcP, byte param) { + if (_musSubroutines.size() < 16) { + const byte *returnP = srcP + 2; + srcP = _musStartPtr + READ_LE_UINT16(srcP); + + _musSubroutines.push(Subroutine(returnP, srcP)); + } + + return false; } -void Music::loadEffectsData() { - File file("admus"); - Common::String md5str = Common::computeStreamMD5AsString(file, 8192); - - if (md5str != "be8989a5e868913f0e53963046e3ea13") - error("Unknown music driver encountered"); +bool MusicDriver::musSetCountdown(const byte *&srcP, byte param) { + // Set the countdown timer + if (!param) + param = *++srcP; + _musCountdownTimer = param; + _musDataPtr = srcP; - // Load in the driver data - const int EFFECTS_OFFSET = 0x91D; - byte *effectsData = new byte[file.size() - EFFECTS_OFFSET]; - file.seek(EFFECTS_OFFSET); - file.read(effectsData, file.size() - EFFECTS_OFFSET); - file.close(); - _effectsData = effectsData; + // Do post-processing and stop processing + postProcess(); + return true; +} - // Extract the effects offsets - _effectsOffsets.resize(180); - for (int idx = 0; idx < 180; ++idx) - _effectsOffsets[idx] = READ_LE_UINT16(&effectsData[idx * 2]) - EFFECTS_OFFSET; +bool MusicDriver::cmdNoOperation(const byte *&srcP, byte param) { + return false; } -void Music::onTimer() { - Common::StackLock slock(_driverMutex); - update(); - flush(); +bool MusicDriver::musSkipWord(const byte *&srcP, byte param) { + srcP += 2; + return false; } -void Music::write(int reg, int val) { - _queue.push(RegisterValue(reg, val)); + +bool MusicDriver::cmdClearFlag(const byte *&srcP, byte param) { + _flags[param] = false; + return false; } -void Music::flush() { - Common::StackLock slock(_driverMutex); +bool MusicDriver::cmdWibbly(const byte *&srcP, byte param) { + if (param != 7 || !_fieldF) { + _field15C[param] = *srcP++; + _field177[param] = 0xFF; + _flags[param] = true; + _field165[param] = READ_BE_UINT16(srcP); + srcP += 2; + } else { + srcP += 3; + } - while (!_queue.empty()) { - RegisterValue v = _queue.pop(); - _opl->writeReg(v._regNum, v._value); + return true; +} + +bool MusicDriver::musEndSubroutine(const byte *&srcP, byte param) { + if (param != 15) { + _field1E = 0; + return true; } + + srcP = _musSubroutines.empty() ? _musStartPtr : _musSubroutines.pop()._returnP; + return false; } -void Music::update() { - const byte *srcP = _dataPtr; +bool MusicDriver::fxCallSubroutine(const byte *&srcP, byte param) { + if (_fxSubroutines.size() < 16) { + const byte *startP = srcP + 2; + srcP = _musStartPtr + READ_LE_UINT16(srcP); - bool flag = !_field1E; - if (!flag) { - _field1C = 0; - if (_field116 && --_field116 == 0) - flag = true; - } - if (flag && _lowMusicIgnored) { - srcP = _musicPtr1; - _field1C = 1; - if (!_field117 || --_field117 == 0) - flag = false; + _fxSubroutines.push(Subroutine(startP, srcP)); } - if (flag) { - postProcess(); - return; - } + return false; +} - // Main loop - bool breakFlag = false; - while (!breakFlag) { - byte nextByte = *srcP++; - int cmd = (nextByte >> 3) & 15; +bool MusicDriver::fxSetCountdown(const byte *&srcP, byte param) { + // Set the countdown timer + if (!param) + param = *++srcP; + _fxCountdownTimer = param; + _musDataPtr = srcP; + + // Do post-processing and stop processing + postProcess(); + return true; +} - CommandFn fn = (_field1C == 1) ? COMMAND_TABLE2[cmd] : COMMAND_TABLE1[cmd]; - breakFlag = (this->*fn)(srcP, nextByte); +bool MusicDriver::fxEndSubroutine(const byte *&srcP, byte param) { + if (param != 15) { + _lowMusicIgnored = false; + return true; } + + srcP = _fxSubroutines.empty() ? _fxStartPtr : _fxSubroutines.pop()._returnP; + return false; } -void Music::playEffect(uint effectId) { +void MusicDriver::playFX(uint effectId, const byte *data) { if (!_lowMusicIgnored || effectId < 7 || effectId >= 11) { - if (effectId < _effectsOffsets.size()) { - _musicPtr1 = _musicPtr2 = &_effectsData[_effectsOffsets[effectId]]; - _field117 = 0; - _field115 = 0; - _field114 = 0; - reset(); - _lowMusicIgnored = true; - } + _musStartPtr = nullptr; + _fxDataPtr = _fxStartPtr = data; + _fxCountdownTimer = 0; + _flags[7] = _flags[8] = 0; + resetFX(); + _lowMusicIgnored = true; + } +} + + +const CommandFn MusicDriver::MUSIC_COMMANDS[16] = { + &MusicDriver::musCallSubroutine, &MusicDriver::musSetCountdown, + &MusicDriver::musSetInstrument, &MusicDriver::cmdNoOperation, + &MusicDriver::musSetPitchWheel, &MusicDriver::musSkipWord, + &MusicDriver::musSetPanning, &MusicDriver::cmdNoOperation, + &MusicDriver::musFade, &MusicDriver::musStartNote, + &MusicDriver::musSetVolume, &MusicDriver::musInjectMidi, + &MusicDriver::musPlayInstrument, &MusicDriver::cmdClearFlag, + &MusicDriver::cmdWibbly, &MusicDriver::musEndSubroutine +}; + +const CommandFn MusicDriver::FX_COMMANDS[16] = { + &MusicDriver::fxCallSubroutine, &MusicDriver::fxSetCountdown, + &MusicDriver::fxSetInstrument, &MusicDriver::fxSetVolume, + &MusicDriver::fxMidiReset, &MusicDriver::fxMidiDword, + &MusicDriver::fxSetPanning, &MusicDriver::fxChannelOff, + &MusicDriver::fxFade, &MusicDriver::fxStartNote, + &MusicDriver::cmdNoOperation, &MusicDriver::fxInjectMidi, + &MusicDriver::fxPlayInstrument, &MusicDriver::cmdClearFlag, + &MusicDriver::cmdWibbly, &MusicDriver::fxEndSubroutine +}; + +/*------------------------------------------------------------------------*/ + +AdlibMusicDriver::AdlibMusicDriver() : _field180(0), _field182(0), _volume(127) { + Common::fill(&_musInstrumentPtrs[0], &_musInstrumentPtrs[16], (const byte *)nullptr); + Common::fill(&_fxInstrumentPtrs[0], &_fxInstrumentPtrs[16], (const byte *)nullptr); + Common::fill(&_frequencies[0], &_frequencies[7], 0); + Common::fill(&_volumes[0], &_volumes[CHANNEL_COUNT], 0); + Common::fill(&_scalingValues[0], &_scalingValues[CHANNEL_COUNT], 0); + + _opl = OPL::Config::create(); + _opl->init(); + _opl->start(new Common::Functor0Mem<void, AdlibMusicDriver>(this, &AdlibMusicDriver::onTimer), CALLBACKS_PER_SECOND); + initialize(); +} + +AdlibMusicDriver::~AdlibMusicDriver() { + _opl->stop(); + delete _opl; +} + +void AdlibMusicDriver::onTimer() { + Common::StackLock slock(_driverMutex); + execute(); + flush(); +} + +void AdlibMusicDriver::initialize() { + write(1, 0x20); + write(8, 0); + write(0xBD, 0); + + resetFrequencies(); + AdlibMusicDriver::resetFX(); +} + +void AdlibMusicDriver::write(int reg, int val) { + _queue.push(RegisterValue(reg, val)); +} + +void AdlibMusicDriver::flush() { + Common::StackLock slock(_driverMutex); + + while (!_queue.empty()) { + RegisterValue v = _queue.pop(); + _opl->writeReg(v._regNum, v._value); } } -void Music::reset() { +void AdlibMusicDriver::resetFX() { if (!_fieldF) { - _field109 = 0; + _frequencies[7] = 0; setFrequency(7, 0); - _channels[7]._outputLevel = 63; + _volumes[7] = 63; setOutputLevel(7, 63); } - _field10B = 0; + _frequencies[8] = 0; setFrequency(8, 0); - _channels[8]._outputLevel = 63; + _volumes[8] = 63; setOutputLevel(8, 63); } -void Music::resetFrequencies() { +void AdlibMusicDriver::resetFrequencies() { for (int opNum = 6; opNum >= 0; --opNum) { - _fieldFB[opNum] = 0; + _frequencies[opNum] = 0; setFrequency(opNum, 0); } } -void Music::setFrequency(byte operatorNum, uint frequency) { +void AdlibMusicDriver::setFrequency(byte operatorNum, uint frequency) { write(0xA0 + operatorNum, frequency & 0xff); write(0xB0 + operatorNum, (frequency >> 8)); } -void Music::postProcess() { - // TODO +uint AdlibMusicDriver::calcFrequency(byte note) { + return WAVEFORMS[note & 0x1F] + ((note & 0xE0) << 5); } -bool Music::cmd1(const byte *&srcP, byte nextByte) { - return false; // TODO +void AdlibMusicDriver::setOutputLevel(byte channelNum, uint level) { + write(0x40 + OPERATOR2_INDEXES[channelNum], level | + (_scalingValues[channelNum] & 0xC0)); } -bool Music::cmd2(const byte *&srcP, byte nextByte) { - return false; // TODO -} +void AdlibMusicDriver::playInstrument(byte channelNum, const byte *data) { + byte op1 = OPERATOR1_INDEXES[channelNum]; + byte op2 = OPERATOR2_INDEXES[channelNum]; -bool Music::cmd3(const byte *&srcP, byte nextByte) { - return false; // TODO -} + write(0x20 + op1, *data++); + write(0x40 + op1, *data++); + write(0x60 + op1, *data++); + write(0x80 + op1, *data++); + write(0xE0 + op1, *data++); + write(0x20 + op2, *data++); -bool Music::cmd4(const byte *&srcP, byte nextByte) { - return false; // TODO -} + int scalingVal = *data++; + _scalingValues[channelNum] = scalingVal; + scalingVal += (127 - _volume) / 2; -bool Music::cmd5(const byte *&srcP, byte nextByte) { - return false; // TODO -} + if (scalingVal > 63) { + scalingVal = 63; + if (_field180) + scalingVal = (scalingVal & 0xC0) | _field182; + } + write(0x40 + op2, scalingVal); -bool Music::cmd6(const byte *&srcP, byte nextByte) { - return false; // TODO + write(0x60 + op2, *data++); + write(0x80 + op2, *data++); + write(0xE0 + op2, *data++); + write(0xC0 + op2, *data++); } -bool Music::cmd7(const byte *&srcP, byte nextByte) { - return false; // TODO -} +bool AdlibMusicDriver::musSetInstrument(const byte *&srcP, byte param) { + _musInstrumentPtrs[param] = srcP; + srcP += 26; -bool Music::cmd8(const byte *&srcP, byte nextByte) { - return false; // TODO + return false; } -bool Music::cmd9(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::musSetPitchWheel(const byte *&srcP, byte param) { + // Adlib does not support this + srcP += 2; + return false; } -bool Music::cmd10(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::musSetPanning(const byte *&srcP, byte param) { + // Adlib does not support this + ++srcP; + return false; } -bool Music::cmd11(const byte *&srcP, byte nextByte) { - return false; // TODO -} +bool AdlibMusicDriver::musFade(const byte *&srcP, byte param) { + ++srcP; + if (param < 7) + setFrequency(param, _frequencies[param]); -bool Music::cmd12(const byte *&srcP, byte nextByte) { - return false; // TODO + return false; } -bool Music::cmd13(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::musStartNote(const byte *&srcP, byte param) { + if (param < 7) { + byte note = *srcP++; + ++srcP; // Second byte is fade, which is unused by Adlib + uint freq = calcFrequency(note); + setFrequency(param, freq); + _frequencies[param] = freq | 0x2000; + setFrequency(param, freq); + } else { + srcP += 2; + } + + return false; } -bool Music::cmd14(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::musSetVolume(const byte *&srcP, byte param) { + if (*srcP++ == 2 && !_field180) { + _volumes[param] = *srcP; + setOutputLevel(param, *srcP); + } + + ++srcP; + return false; } -bool Music::cmd15(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::musInjectMidi(const byte *&srcP, byte param) { + // Adlib does not support MIDI. So simply keep skipping over bytes + // until an 'F7' byte is found that flags the end of the MIDI data + while (*srcP++ != 0xF7) + ; + + return false; } -bool Music::cmd16(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::musPlayInstrument(const byte *&srcP, byte param) { + if (param < 7) + playInstrument(param, _musInstrumentPtrs[param]); + + return false; } -bool Music::cmd17(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::fxSetInstrument(const byte *&srcP, byte param) { + _fxInstrumentPtrs[param] = srcP; + srcP += 11; + + return false; } -bool Music::cmd18(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::fxSetVolume(const byte *&srcP, byte param) { + if (!_field180 && (!_fieldF || param != 7)) { + _volumes[param] = *srcP; + setOutputLevel(param, *srcP); + } + + ++srcP; + return false; } -bool Music::cmd19(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::fxMidiReset(const byte *&srcP, byte param) { + return false; } -bool Music::cmd20(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::fxMidiDword(const byte *&srcP, byte param) { + return false; } -bool Music::cmd21(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::fxSetPanning(const byte *&srcP, byte param) { + byte note = *srcP++; + if (!_fieldF || param != 7) { + uint freq = calcFrequency(note); + setFrequency(param, freq); + _frequencies[param] = freq; + } + + return false; } -bool Music::cmd22(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::fxChannelOff(const byte *&srcP, byte param) { + _frequencies[param] &= ~0x2000; + write(0xB0 + param, _frequencies[param]); + return false; } -bool Music::cmd23(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::fxFade(const byte *&srcP, byte param) { + uint freq = calcFrequency(*srcP++); + if (!_fieldF || param != 7) { + _frequencies[param] = freq; + setFrequency(param, freq); + } + + return false; } -bool Music::cmd24(const byte *&srcP, byte nextByte) { - return false; // TODO +bool AdlibMusicDriver::fxStartNote(const byte *&srcP, byte param) { + if (!_fieldF || param != 7) { + byte note = *srcP++; + uint freq = calcFrequency(note); + setFrequency(param, freq); + _frequencies[param] = freq | 0x2000; + setFrequency(param, freq); + } else { + ++srcP; + } + + return false; } -const CommandFn Music::COMMAND_TABLE1[16] = { - &Music::cmd1, &Music::cmd2, &Music::cmd3, &Music::cmd4, - &Music::cmd5, &Music::cmd5, &Music::cmd6, &Music::cmd4, - &Music::cmd7, &Music::cmd8, &Music::cmd9, &Music::cmd10, - &Music::cmd11, &Music::cmd12, &Music::cmd13, &Music::cmd14 -}; +bool AdlibMusicDriver::fxInjectMidi(const byte *&srcP, byte param) { + // Surpringly, unlike the musInjectMidi, this version doesn't have + // any logic to skip over following MIDI data. Which must mean the opcode + // and/or it's data aren't present in the admus driver file + return false; +} -const CommandFn Music::COMMAND_TABLE2[16] = { - &Music::cmd15, &Music::cmd16, &Music::cmd17, &Music::cmd18, - &Music::cmd4, &Music::cmd4, &Music::cmd19, &Music::cmd20, - &Music::cmd21, &Music::cmd22, &Music::cmd4, &Music::cmd4, - &Music::cmd23, &Music::cmd12, &Music::cmd13, &Music::cmd24 -}; +bool AdlibMusicDriver::fxPlayInstrument(const byte *&srcP, byte param) { + if (!_fieldF || param != 7) + playInstrument(param, _fxInstrumentPtrs[param]); -void Music::setOutputLevel(byte channelNum, uint level) { - write(0x40 + OPERATOR2_INDEXES[channelNum], level | - (_channels[channelNum]._scalingValue & 0xC0)); + return false; } -const byte Music::OPERATOR1_INDEXES[ADLIB_CHANNEL_COUNT] = { +const byte AdlibMusicDriver::OPERATOR1_INDEXES[CHANNEL_COUNT] = { 0, 1, 2, 8, 9, 0xA, 0x10, 0x11, 0x12 }; -const byte Music::OPERATOR2_INDEXES[ADLIB_CHANNEL_COUNT] = { +const byte AdlibMusicDriver::OPERATOR2_INDEXES[CHANNEL_COUNT] = { 3, 4, 5, 0xB, 0xC, 0xD, 0x13, 0x14, 0x15 }; +const uint AdlibMusicDriver::WAVEFORMS[24] = { + 0, 347, 388, 436, 462, 519, 582, 646, + 0, 362, 406, 455, 484, 542, 607, 680, + 0, 327, 367, 412, 436, 489, 549, 618 +}; + +/*------------------------------------------------------------------------*/ + +Music::Music(Audio::Mixer *mixer) : _mixer(mixer), _musicDriver(nullptr) { + _mixer = mixer; + loadEffectsData(); +} + +Music::~Music() { + delete[] _effectsData; +} + +void Music::loadEffectsData() { + File file("admus"); + Common::String md5str = Common::computeStreamMD5AsString(file, 8192); + + if (md5str != "be8989a5e868913f0e53963046e3ea13") + error("Unknown music driver encountered"); + + // Load in the driver data + byte *effectsData = new byte[file.size()]; + file.seek(0); + file.read(effectsData, file.size()); + file.close(); + _effectsData = effectsData; + + // Extract the effects offsets + _effectsOffsets.resize(180); + const int EFFECTS_OFFSET = 0x91D; + for (int idx = 0; idx < 180; ++idx) + _effectsOffsets[idx] = READ_LE_UINT16(&effectsData[EFFECTS_OFFSET + idx * 2]); +} + +void Music::playEffect(uint effectId) { + if (effectId < _effectsOffsets.size()) { + const byte *dataP = &_effectsData[_effectsOffsets[effectId]]; + _musicDriver->playFX(effectId, dataP); + } +} + } // End of namespace Xeen diff --git a/engines/xeen/music.h b/engines/xeen/music.h index b1fa43daa2..0d5188e697 100644 --- a/engines/xeen/music.h +++ b/engines/xeen/music.h @@ -28,8 +28,9 @@ #include "common/array.h" #include "common/mutex.h" #include "common/queue.h" +#include "common/stack.h" -#define ADLIB_CHANNEL_COUNT 9 +#define CHANNEL_COUNT 9 namespace OPL { class OPL; @@ -37,52 +38,132 @@ namespace OPL { namespace Xeen { -class Music; +class MusicDriver; -typedef bool (Music::*CommandFn)(const byte *&srcP, byte nextByte); +typedef bool (MusicDriver::*CommandFn)(const byte *&srcP, byte param); -struct RegisterValue { - uint8 _regNum; - uint8 _value; +/** + * Base class for music drivers + */ +class MusicDriver { + struct Subroutine { + const byte *_returnP; + const byte *_jumpP; + Subroutine() : _returnP(nullptr), _jumpP(nullptr) {} + Subroutine(const byte *returnP, const byte *endP) : + _returnP(returnP), _jumpP(endP) {} + }; +private: + static const CommandFn FX_COMMANDS[16]; + static const CommandFn MUSIC_COMMANDS[16]; +private: + Common::Stack<Subroutine> _musSubroutines, _fxSubroutines; + bool _field1E; + int _musCountdownTimer; + int _fxCountdownTimer; + bool _lowMusicIgnored; + const byte *_fxDataPtr, *_musDataPtr; + const byte *_fxStartPtr; + const byte *_musStartPtr; + bool _flags[CHANNEL_COUNT]; + byte _field15C[CHANNEL_COUNT]; + byte _field165[CHANNEL_COUNT]; + byte _field177[CHANNEL_COUNT]; +private: + /** + * Executes the next command + * @param srcP Command data pointer + * @returns If true, execution of commands for the current timer call stops + */ + bool command(const byte *&srcP); +protected: + bool _fieldF; +protected: + /** + * Executes a series of commands until instructed to stop + */ + void execute(); + + // Music commands (with some also used by FX) + virtual bool musCallSubroutine(const byte *&srcP, byte param); + virtual bool musSetCountdown(const byte *&srcP, byte param); + virtual bool musSetInstrument(const byte *&srcP, byte param) = 0; + virtual bool cmdNoOperation(const byte *&srcP, byte param); + virtual bool musSetPitchWheel(const byte *&srcP, byte param) = 0; + virtual bool musSkipWord(const byte *&srcP, byte param); + virtual bool musSetPanning(const byte *&srcP, byte param) = 0; + virtual bool musFade(const byte *&srcP, byte param) = 0; + virtual bool musStartNote(const byte *&srcP, byte param) = 0; + virtual bool musSetVolume(const byte *&srcP, byte param) = 0; + virtual bool musInjectMidi(const byte *&srcP, byte param) = 0; + virtual bool musPlayInstrument(const byte *&srcP, byte param) = 0; + virtual bool cmdClearFlag(const byte *&srcP, byte param); + virtual bool cmdWibbly(const byte *&srcP, byte param); + virtual bool musEndSubroutine(const byte *&srcP, byte param); + + // FX commands + virtual bool fxCallSubroutine(const byte *&srcP, byte param); + virtual bool fxSetCountdown(const byte *&srcP, byte param); + virtual bool fxSetInstrument(const byte *&srcP, byte param) = 0; + virtual bool fxSetVolume(const byte *&srcP, byte param) = 0; + virtual bool fxMidiReset(const byte *&srcP, byte param) = 0; + virtual bool fxMidiDword(const byte *&srcP, byte param) = 0; + virtual bool fxSetPanning(const byte *&srcP, byte param) = 0; + virtual bool fxChannelOff(const byte *&srcP, byte param) = 0; + virtual bool fxFade(const byte *&srcP, byte param) = 0; + virtual bool fxStartNote(const byte *&srcP, byte param) = 0; + virtual bool fxInjectMidi(const byte *&srcP, byte param) = 0; + virtual bool fxPlayInstrument(const byte *&srcP, byte param) = 0; + virtual bool fxEndSubroutine(const byte *&srcP, byte param); - RegisterValue(int regNum, int value) { - _regNum = regNum; _value = value; - } + virtual void postProcess() = 0; + + /** + * Does a reset of any sound effect + */ + virtual void resetFX() = 0; +public: + /** + * Constructor + */ + MusicDriver(); + + /** + * Destructor + */ + virtual ~MusicDriver() {} + + /** + * Starts an special effect playing + */ + void playFX(uint effectId, const byte *data); }; -class Music { - struct Channel { - byte _outputLevel; - byte _scalingValue; +class AdlibMusicDriver : public MusicDriver { + struct RegisterValue { + uint8 _regNum; + uint8 _value; - Channel() : _outputLevel(0), _scalingValue(0) {} + RegisterValue(int regNum, int value) { + _regNum = regNum; _value = value; + } }; private: - static const byte OPERATOR1_INDEXES[ADLIB_CHANNEL_COUNT]; - static const byte OPERATOR2_INDEXES[ADLIB_CHANNEL_COUNT]; - static const CommandFn COMMAND_TABLE1[16]; - static const CommandFn COMMAND_TABLE2[16]; + static const byte OPERATOR1_INDEXES[CHANNEL_COUNT]; + static const byte OPERATOR2_INDEXES[CHANNEL_COUNT]; + static const uint WAVEFORMS[24]; private: OPL::OPL *_opl; - Common::Mutex _driverMutex; - Common::Array<Channel> _channels; Common::Queue<RegisterValue> _queue; - const byte *_effectsData; - Common::Array<uint16> _effectsOffsets; - const byte *_musicPtr1, *_musicPtr2; - const byte *_dataPtr; - bool _fieldF; - bool _field1C; - bool _field1E; - uint _fieldFB[7]; - int _field109; - int _field10B; - byte _field10D[7]; - int _field114; - int _field115; - int _field116; - int _field117; - bool _lowMusicIgnored; + Common::Mutex _driverMutex; + byte _volumes[CHANNEL_COUNT]; + byte _scalingValues[CHANNEL_COUNT]; + const byte *_musInstrumentPtrs[16]; + const byte *_fxInstrumentPtrs[16]; + uint _frequencies[7]; + int _field180; + int _field182; + int _volume; private: /** * Initializes the state of the Adlib OPL driver @@ -90,11 +171,6 @@ private: void initialize(); /** - * Loads effects data that was embedded in the music driver - */ - void loadEffectsData(); - - /** * Adds a register write to the pending queue that will be flushed * out to the OPL on the next timer call */ @@ -111,16 +187,6 @@ private: void flush(); /** - * Updates any playing music - */ - void update(); - - /** - * Does a reset - */ - void reset(); - - /** * Resets all the output frequencies */ void resetFrequencies(); @@ -131,42 +197,73 @@ private: void setFrequency(byte operatorNum, uint frequency); /** + * Calculates the frequency for a note + */ + uint calcFrequency(byte note); + + /** * Sets the output level for a channel */ void setOutputLevel(byte channelNum, uint level); /** - * Post-process - */ - void postProcess(); - - /** - * Update command methods - */ - bool cmd1(const byte *&srcP, byte nextByte); - bool cmd2(const byte *&srcP, byte nextByte); - bool cmd3(const byte *&srcP, byte nextByte); - bool cmd4(const byte *&srcP, byte nextByte); - bool cmd5(const byte *&srcP, byte nextByte); - bool cmd6(const byte *&srcP, byte nextByte); - bool cmd7(const byte *&srcP, byte nextByte); - bool cmd8(const byte *&srcP, byte nextByte); - bool cmd9(const byte *&srcP, byte nextByte); - bool cmd10(const byte *&srcP, byte nextByte); - bool cmd11(const byte *&srcP, byte nextByte); - bool cmd12(const byte *&srcP, byte nextByte); - bool cmd13(const byte *&srcP, byte nextByte); - bool cmd14(const byte *&srcP, byte nextByte); - bool cmd15(const byte *&srcP, byte nextByte); - bool cmd16(const byte *&srcP, byte nextByte); - bool cmd17(const byte *&srcP, byte nextByte); - bool cmd18(const byte *&srcP, byte nextByte); - bool cmd19(const byte *&srcP, byte nextByte); - bool cmd20(const byte *&srcP, byte nextByte); - bool cmd21(const byte *&srcP, byte nextByte); - bool cmd22(const byte *&srcP, byte nextByte); - bool cmd23(const byte *&srcP, byte nextByte); - bool cmd24(const byte *&srcP, byte nextByte); + * Starts playing an instrument + */ + void playInstrument(byte channelNum, const byte *data); +protected: + virtual bool musSetInstrument(const byte *&srcP, byte param); + virtual bool musSetPitchWheel(const byte *&srcP, byte param); + virtual bool musSetPanning(const byte *&srcP, byte param); + virtual bool musFade(const byte *&srcP, byte param); + virtual bool musStartNote(const byte *&srcP, byte param); + virtual bool musSetVolume(const byte *&srcP, byte param); + virtual bool musInjectMidi(const byte *&srcP, byte param); + virtual bool musPlayInstrument(const byte *&srcP, byte param); + + virtual bool fxSetInstrument(const byte *&srcP, byte param); + virtual bool fxSetVolume(const byte *&srcP, byte param); + virtual bool fxMidiReset(const byte *&srcP, byte param); + virtual bool fxMidiDword(const byte *&srcP, byte param); + virtual bool fxSetPanning(const byte *&srcP, byte param); + virtual bool fxChannelOff(const byte *&srcP, byte param); + virtual bool fxFade(const byte *&srcP, byte param); + virtual bool fxStartNote(const byte *&srcP, byte param); + virtual bool fxInjectMidi(const byte *&srcP, byte param); + virtual bool fxPlayInstrument(const byte *&srcP, byte param); + + /** + * Does a reset of any sound effect + */ + virtual void resetFX(); +public: + /** + * Constructor + */ + AdlibMusicDriver(); + + /** + * Destructor + */ + virtual ~AdlibMusicDriver(); +}; + + +class Music { +private: + MusicDriver *_musicDriver; + const byte *_effectsData; + Common::Array<uint16> _effectsOffsets; +private: + /** + * Loads effects data that was embedded in the music driver + */ + void loadEffectsData(); + + /** + * Updates any playing music + */ + void update(); + protected: Audio::Mixer *_mixer; public: |