diff options
Diffstat (limited to 'audio')
-rw-r--r-- | audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp | 1003 | ||||
-rw-r--r-- | audio/softsynth/fmtowns_pc98/towns_pc98_driver.h | 22 |
2 files changed, 504 insertions, 521 deletions
diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp index 091905b18e..b5e8c84491 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp @@ -23,35 +23,30 @@ #include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h" #include "common/endian.h" #include "common/textconsole.h" +#include "common/func.h" +#include "common/array.h" class TownsPC98_MusicChannel { public: - TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, - uint8 key, uint8 prt, uint8 id); + TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); virtual ~TownsPC98_MusicChannel(); - virtual void init(); typedef enum channelState { CHS_RECALCFREQ = 0x01, - CHS_KEYOFF = 0x02, - CHS_SSGOFF = 0x04, - CHS_VBROFF = 0x08, - CHS_ALLOFF = 0x0f, - CHS_PROTECT = 0x40, - CHS_EOT = 0x80 + CHS_KEYOFF = 0x02, + CHS_SSGOFF = 0x04, + CHS_VBROFF = 0x08, + CHS_ALLOFF = 0x0f, + CHS_PROTECT = 0x40, + CHS_EOT = 0x80 } ChannelState; + virtual void reset(); virtual void loadData(uint8 *data); virtual void processEvents(); virtual void processFrequency(); - virtual bool processControlEvent(uint8 cmd); - - virtual void keyOn(); - void keyOff(); - - void setOutputLevel(); - virtual void fadeStep(); - virtual void reset(); + + virtual void fadeStep(); const uint8 _idFlag; @@ -59,31 +54,52 @@ protected: void setupVibrato(); bool processVibrato(); + uint8 readReg(uint8 part, uint8 reg); + void writeReg(uint8 part, uint8 reg, uint8 val); + bool control_dummy(uint8 para); - bool control_f0_setPatch(uint8 para); - bool control_f1_presetOutputLevel(uint8 para); - bool control_f2_setKeyOffTime(uint8 para); - bool control_f3_setFreqLSB(uint8 para); - bool control_f4_setOutputLevel(uint8 para); - bool control_f5_setTempo(uint8 para); + bool control_f2_duration(uint8 para); + bool control_f3_pitchBend(uint8 para); + bool control_f5_tempo(uint8 para); bool control_f6_repeatSection(uint8 para); bool control_f7_setupVibrato(uint8 para); bool control_f8_toggleVibrato(uint8 para); bool control_fa_writeReg(uint8 para); - virtual bool control_fb_incOutLevel(uint8 para); - virtual bool control_fc_decOutLevel(uint8 para); bool control_fd_jump(uint8 para); - virtual bool control_ff_endOfTrack(uint8 para); uint8 _ticksLeft; - uint8 _algorithm; + uint8 _duration; uint8 _instr; uint8 _totalLevel; uint8 _frqBlockMSB; - int8 _frqLSB; - uint8 _keyOffTime; - bool _hold; + uint16 _frequency; + uint8 _block; + int8 _pitchBend; + uint8 _regOffset; + uint8 _flags; uint8 *_dataPtr; + bool _sustain; + bool _fading; + + TownsPC98_AudioDriver *_driver; + const uint8 _chanNum; + + static const uint8 _controlEventSize[16]; + +private: + void keyOn(); + void keyOff(); + + void setOutputLevel(); + bool processControlEvent(uint8 cmd); + + bool control_f0_setPatch(uint8 para); + bool control_f1_presetOutputLevel(uint8 para); + bool control_f4_setOutputLevel(uint8 para); + bool control_fb_incOutLevel(uint8 para); + bool control_fc_decOutLevel(uint8 para); + bool control_ff_endOfTrack(uint8 para); + uint8 _vbrInitDelayHi; uint8 _vbrInitDelayLo; int16 _vbrModInitVal; @@ -91,49 +107,36 @@ protected: uint8 _vbrCurDelay; int16 _vbrModCurVal; uint8 _vbrDurLeft; - uint16 _frequency; - uint8 _block; - uint8 _regOffset; - uint8 _flags; - uint8 _ssgTl; - uint8 _ssgStep; - uint8 _ssgTicksLeft; - uint8 _ssgTargetLvl; - uint8 _ssgStartLvl; - - const uint8 _chanNum; + uint8 _algorithm; + const uint8 _keyNum; const uint8 _part; - TownsPC98_AudioDriver *_drv; - - typedef bool (TownsPC98_MusicChannel::*ControlEventFunc)(uint8 para); - const ControlEventFunc *controlEvents; + typedef Common::Functor1Mem<uint8, bool, TownsPC98_MusicChannel> ControlEvent; + Common::Array<const ControlEvent*> _controlEvents; }; class TownsPC98_MusicChannelSSG : public TownsPC98_MusicChannel { public: - TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs, - uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); - virtual ~TownsPC98_MusicChannelSSG() {} - void init(); + TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); + virtual ~TownsPC98_MusicChannelSSG(); + virtual void reset(); virtual void loadData(uint8 *data); void processEvents(); void processFrequency(); - bool processControlEvent(uint8 cmd); - - void keyOn(); - void nextShape(); void protect(); void restore(); - virtual void reset(); void fadeStep(); protected: + void keyOn(); + void nextShape(); + void setOutputLevel(uint8 lvl); + bool processControlEvent(uint8 cmd); bool control_f0_setPatch(uint8 para); bool control_f1_setTotalLevel(uint8 para); @@ -143,101 +146,103 @@ protected: bool control_fc_decOutLevel(uint8 para); bool control_ff_endOfTrack(uint8 para); - typedef bool (TownsPC98_MusicChannelSSG::*ControlEventFunc)(uint8 para); - const ControlEventFunc *controlEvents; + uint8 _ssgTl; + uint8 _ssgStep; + uint8 _ssgTicksLeft; + uint8 _ssgTargetLvl; + uint8 _ssgStartLvl; + uint8 _algorithm; + + static uint8 *_envPatchData; + static const uint8 _envData[256]; + + typedef Common::Functor1Mem<uint8, bool, TownsPC98_MusicChannelSSG> ControlEvent; + Common::Array<const ControlEvent*> _controlEvents; }; class TownsPC98_SfxChannel : public TownsPC98_MusicChannelSSG { public: - TownsPC98_SfxChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, - uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : + TownsPC98_SfxChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : TownsPC98_MusicChannelSSG(driver, regOffs, flgs, num, key, prt, id) {} - ~TownsPC98_SfxChannel() {} + virtual ~TownsPC98_SfxChannel() {} - void loadData(uint8 *data); void reset(); + void loadData(uint8 *data); }; #ifndef DISABLE_PC98_RHYTHM_CHANNEL class TownsPC98_MusicChannelPCM : public TownsPC98_MusicChannel { public: - TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs, - uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); - ~TownsPC98_MusicChannelPCM() {} - void init(); + TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); + virtual ~TownsPC98_MusicChannelPCM(); void loadData(uint8 *data); void processEvents(); - bool processControlEvent(uint8 cmd); private: + bool processControlEvent(uint8 cmd); bool control_f1_prcStart(uint8 para); bool control_ff_endOfTrack(uint8 para); - typedef bool (TownsPC98_MusicChannelPCM::*ControlEventFunc)(uint8 para); - const ControlEventFunc *controlEvents; + uint8 _algorithm; + + typedef Common::Functor1Mem<uint8, bool, TownsPC98_MusicChannelPCM> ControlEvent; + Common::Array<const ControlEvent*> _controlEvents; }; #endif -TownsPC98_MusicChannel::TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, - uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key), - _part(prt), _idFlag(id), controlEvents(0) { - - _ticksLeft = _algorithm = _instr = _totalLevel = _frqBlockMSB = _keyOffTime = 0; - _ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = _block = 0; - _vbrInitDelayHi = _vbrInitDelayLo = _vbrDuration = _vbrCurDelay = _vbrDurLeft = 0; - _frqLSB = 0; - _hold = false; - _dataPtr = 0; - _vbrModInitVal = _vbrModCurVal = 0; - _frequency = 0; -} +#define CONTROL(x) _controlEvents.push_back(new ControlEvent(this, &TownsPC98_MusicChannel::control_##x)) +TownsPC98_MusicChannel::TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : _driver(driver), +_regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key), _part(prt), _idFlag(id), _ticksLeft(0), _algorithm(0), _instr(0), _totalLevel(0), +_frqBlockMSB(0), _duration(0), _block(0), _vbrInitDelayHi(0), _vbrInitDelayLo(0), _vbrDuration(0), _vbrCurDelay(0), _vbrDurLeft(0), _pitchBend(0), +_sustain(false), _fading(false), _dataPtr(0), _vbrModInitVal(0), _vbrModCurVal(0), _frequency(0) { + CONTROL(f0_setPatch); + CONTROL(f1_presetOutputLevel); + CONTROL(f2_duration); + CONTROL(f3_pitchBend); + CONTROL(f4_setOutputLevel); + CONTROL(f5_tempo); + CONTROL(f6_repeatSection); + CONTROL(f7_setupVibrato); + CONTROL(f8_toggleVibrato); + CONTROL(dummy); + CONTROL(fa_writeReg); + CONTROL(fb_incOutLevel); + CONTROL(fc_decOutLevel); + CONTROL(fd_jump); + CONTROL(dummy); + CONTROL(ff_endOfTrack); +} +#undef CONTROL TownsPC98_MusicChannel::~TownsPC98_MusicChannel() { + for (Common::Array<const ControlEvent*>::iterator i = _controlEvents.begin(); i != _controlEvents.end(); ++i) + delete *i; } -void TownsPC98_MusicChannel::init() { -#define Control(x) &TownsPC98_MusicChannel::control_##x - static const ControlEventFunc ctrlEvents[] = { - Control(f0_setPatch), - Control(f1_presetOutputLevel), - Control(f2_setKeyOffTime), - Control(f3_setFreqLSB), - Control(f4_setOutputLevel), - Control(f5_setTempo), - Control(f6_repeatSection), - Control(f7_setupVibrato), - Control(f8_toggleVibrato), - Control(dummy), - Control(fa_writeReg), - Control(fb_incOutLevel), - Control(fc_decOutLevel), - Control(fd_jump), - Control(dummy), - Control(ff_endOfTrack) - }; -#undef Control - - controlEvents = ctrlEvents; -} +void TownsPC98_MusicChannel::reset() { + _sustain = false; + _duration = 0; + _fading = false; + _ticksLeft = 1; -void TownsPC98_MusicChannel::keyOff() { - // all operators off - uint8 value = _keyNum & 0x0f; - if (_part) - value |= 4; - uint8 regAddress = 0x28; - _drv->writeReg(0, regAddress, value); - _flags |= CHS_KEYOFF; -} + _flags = (_flags & ~CHS_EOT) | CHS_ALLOFF; -void TownsPC98_MusicChannel::keyOn() { - // all operators on - uint8 value = _keyNum | 0xf0; - if (_part) - value |= 4; - uint8 regAddress = 0x28; - _drv->writeReg(0, regAddress, value); + _totalLevel = 0; + _algorithm = 0; + + _block = 0; + _frequency = 0; + _frqBlockMSB = 0; + _pitchBend = 0; + + _vbrInitDelayHi = 0; + _vbrInitDelayLo = 0; + _vbrModInitVal = 0; + _vbrDuration = 0; + _vbrCurDelay = 0; + _vbrModCurVal = 0; + _vbrDurLeft = 0; } void TownsPC98_MusicChannel::loadData(uint8 *data) { @@ -253,8 +258,8 @@ void TownsPC98_MusicChannel::loadData(uint8 *data) { tmp++; } else if (cmd == 0xff) { if (READ_LE_UINT16(tmp)) { - _drv->_looping |= _idFlag; - tmp += _drv->_opnFxCmdLen[cmd - 240]; + _driver->_looping |= _idFlag; + tmp += _controlEventSize[cmd - 240]; } else loop = false; } else if (cmd == 0xf6) { @@ -262,7 +267,7 @@ void TownsPC98_MusicChannel::loadData(uint8 *data) { tmp[0] = tmp[1]; tmp += 4; } else { - tmp += _drv->_opnFxCmdLen[cmd - 240]; + tmp += _controlEventSize[cmd - 240]; } } } @@ -271,13 +276,13 @@ void TownsPC98_MusicChannel::processEvents() { if (_flags & CHS_EOT) return; - if (!_hold && _ticksLeft == _keyOffTime) + if (!_sustain && _ticksLeft == _duration) keyOff(); if (--_ticksLeft) return; - if (!_hold) + if (!_sustain) keyOff(); uint8 cmd = 0; @@ -295,14 +300,14 @@ void TownsPC98_MusicChannel::processEvents() { if (cmd == 0x80) { keyOff(); - _hold = false; + _sustain = false; } else { keyOn(); - if (_hold == false || cmd != _frqBlockMSB) + if (_sustain == false || cmd != _frqBlockMSB) _flags |= CHS_RECALCFREQ; - _hold = (para & 0x80) ? true : false; + _sustain = (para & 0x80) ? true : false; _frqBlockMSB = cmd; } @@ -310,12 +315,14 @@ void TownsPC98_MusicChannel::processEvents() { } void TownsPC98_MusicChannel::processFrequency() { + static const uint16 noteFrequencies[] = { 0x26a, 0x28f, 0x2b6, 0x2df, 0x30b, 0x339, 0x36a, 0x39e, 0x3d5, 0x410, 0x44e, 0x48f }; + if (_flags & CHS_RECALCFREQ) { - _frequency = (READ_LE_UINT16(&_drv->_opnFreqTable[(_frqBlockMSB & 0x0f) << 1]) + _frqLSB) | (((_frqBlockMSB & 0x70) >> 1) << 8); + _frequency = (noteFrequencies[_frqBlockMSB & 0x0f] + _pitchBend) | (((_frqBlockMSB & 0x70) >> 1) << 8); - _drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8)); - _drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff)); + writeReg(_part, _regOffset + 0xa4, (_frequency >> 8)); + writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff)); setupVibrato(); } @@ -324,11 +331,19 @@ void TownsPC98_MusicChannel::processFrequency() { if (!processVibrato()) return; - _drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8)); - _drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff)); + writeReg(_part, _regOffset + 0xa4, (_frequency >> 8)); + writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff)); } } +void TownsPC98_MusicChannel::fadeStep() { + _fading = true; + _totalLevel += 3; + if (_totalLevel > 0x7f) + _totalLevel = 0x7f; + setOutputLevel(); +} + void TownsPC98_MusicChannel::setupVibrato() { _vbrCurDelay = _vbrInitDelayHi; if (_flags & CHS_KEYOFF) { @@ -354,125 +369,31 @@ bool TownsPC98_MusicChannel::processVibrato() { return true; } -bool TownsPC98_MusicChannel::processControlEvent(uint8 cmd) { - uint8 para = *_dataPtr++; - return (this->*controlEvents[cmd & 0x0f])(para); -} - -void TownsPC98_MusicChannel::setOutputLevel() { - uint8 outopr = _drv->_opnCarrier[_algorithm]; - uint8 reg = 0x40 + _regOffset; - - for (int i = 0; i < 4; i++) { - if (outopr & 1) - _drv->writeReg(_part, reg, _totalLevel); - outopr >>= 1; - reg += 4; - } -} - -void TownsPC98_MusicChannel::fadeStep() { - _totalLevel += 3; - if (_totalLevel > 0x7f) - _totalLevel = 0x7f; - setOutputLevel(); +uint8 TownsPC98_MusicChannel::readReg(uint8 part, uint8 reg) { + return _driver->readReg(part, reg); } -void TownsPC98_MusicChannel::reset() { - _hold = false; - _keyOffTime = 0; - _ticksLeft = 1; - - _flags = (_flags & ~CHS_EOT) | CHS_ALLOFF; - - _totalLevel = 0; - _algorithm = 0; - - _block = 0; - _frequency = 0; - _frqBlockMSB = 0; - _frqLSB = 0; - - _ssgTl = 0; - _ssgStartLvl = 0; - _ssgTargetLvl = 0; - _ssgStep = 0; - _ssgTicksLeft = 0; - - _vbrInitDelayHi = 0; - _vbrInitDelayLo = 0; - _vbrModInitVal = 0; - _vbrDuration = 0; - _vbrCurDelay = 0; - _vbrModCurVal = 0; - _vbrDurLeft = 0; +void TownsPC98_MusicChannel::writeReg(uint8 part, uint8 reg, uint8 val) { + _driver->writeReg(part, reg, val); } -bool TownsPC98_MusicChannel::control_f0_setPatch(uint8 para) { - _instr = para; - uint8 reg = _regOffset + 0x80; - - for (int i = 0; i < 4; i++) { - // set release rate for each operator - _drv->writeReg(_part, reg, 0x0f); - reg += 4; - } - - const uint8 *tptr = _drv->_patches + ((uint32)_instr << 5); - reg = _regOffset + 0x30; - - // write registers 0x30 to 0x8f - for (int i = 0; i < 6; i++) { - _drv->writeReg(_part, reg, tptr[0]); - reg += 4; - _drv->writeReg(_part, reg, tptr[2]); - reg += 4; - _drv->writeReg(_part, reg, tptr[1]); - reg += 4; - _drv->writeReg(_part, reg, tptr[3]); - reg += 4; - tptr += 4; - } - - reg = _regOffset + 0xB0; - _algorithm = tptr[0] & 7; - // set feedback and algorithm - _drv->writeReg(_part, reg, tptr[0]); - - setOutputLevel(); - return true; -} - -bool TownsPC98_MusicChannel::control_f1_presetOutputLevel(uint8 para) { - if (_drv->_fading) - return true; - - _totalLevel = _drv->_opnLvlPresets[para]; - setOutputLevel(); - return true; -} - -bool TownsPC98_MusicChannel::control_f2_setKeyOffTime(uint8 para) { - _keyOffTime = para; +bool TownsPC98_MusicChannel::control_dummy(uint8 para) { + _dataPtr--; return true; } -bool TownsPC98_MusicChannel::control_f3_setFreqLSB(uint8 para) { - _frqLSB = (int8) para; +bool TownsPC98_MusicChannel::control_f2_duration(uint8 para) { + _duration = para; return true; } -bool TownsPC98_MusicChannel::control_f4_setOutputLevel(uint8 para) { - if (_drv->_fading) - return true; - - _totalLevel = para; - setOutputLevel(); +bool TownsPC98_MusicChannel::control_f3_pitchBend(uint8 para) { + _pitchBend = (int8) para; return true; } -bool TownsPC98_MusicChannel::control_f5_setTempo(uint8 para) { - _drv->setMusicTempo(para); +bool TownsPC98_MusicChannel::control_f5_tempo(uint8 para) { + _driver->setMusicTempo(para); return true; } @@ -482,7 +403,7 @@ bool TownsPC98_MusicChannel::control_f6_repeatSection(uint8 para) { if (*_dataPtr) { // repeat section until counter has reached zero - _dataPtr = _drv->_trackPtr + READ_LE_UINT16(_dataPtr + 2); + _dataPtr = _driver->_trackPtr + READ_LE_UINT16(_dataPtr + 2); } else { // reset counter, advance to next section _dataPtr[0] = _dataPtr[1]; @@ -520,13 +441,109 @@ bool TownsPC98_MusicChannel::control_f8_toggleVibrato(uint8 para) { } bool TownsPC98_MusicChannel::control_fa_writeReg(uint8 para) { - _drv->writeReg(_part, para, *_dataPtr++); + writeReg(_part, para, *_dataPtr++); + return true; +} + +bool TownsPC98_MusicChannel::control_fd_jump(uint8 para) { + uint8 *tmp = _driver->_trackPtr + READ_LE_UINT16(_dataPtr - 1); + _dataPtr = (tmp[1] == 1) ? tmp : (_dataPtr + 1); + return true; +} + +void TownsPC98_MusicChannel::keyOn() { + // all operators on + uint8 value = _keyNum | 0xf0; + if (_part) + value |= 4; + uint8 regAddress = 0x28; + writeReg(0, regAddress, value); +} + +void TownsPC98_MusicChannel::keyOff() { + // all operators off + uint8 value = _keyNum & 0x0f; + if (_part) + value |= 4; + uint8 regAddress = 0x28; + writeReg(0, regAddress, value); + _flags |= CHS_KEYOFF; +} + +void TownsPC98_MusicChannel::setOutputLevel() { + static const uint8 carrier[] = { 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F }; + uint8 outopr = carrier[_algorithm]; + uint8 reg = 0x40 + _regOffset; + + for (int i = 0; i < 4; i++) { + if (outopr & 1) + writeReg(_part, reg, _totalLevel); + outopr >>= 1; + reg += 4; + } +} + +bool TownsPC98_MusicChannel::processControlEvent(uint8 cmd) { + uint8 para = *_dataPtr++; + return (*_controlEvents[cmd & 0x0f])(para); +} + +bool TownsPC98_MusicChannel::control_f0_setPatch(uint8 para) { + _instr = para; + uint8 reg = _regOffset + 0x80; + + for (int i = 0; i < 4; i++) { + // set release rate for each operator + writeReg(_part, reg, 0x0f); + reg += 4; + } + + const uint8 *tptr = _driver->_patchData + ((uint32)_instr << 5); + reg = _regOffset + 0x30; + + // write registers 0x30 to 0x8f + for (int i = 0; i < 6; i++) { + writeReg(_part, reg, tptr[0]); + reg += 4; + writeReg(_part, reg, tptr[2]); + reg += 4; + writeReg(_part, reg, tptr[1]); + reg += 4; + writeReg(_part, reg, tptr[3]); + reg += 4; + tptr += 4; + } + + reg = _regOffset + 0xB0; + _algorithm = tptr[0] & 7; + // set feedback and algorithm + writeReg(_part, reg, tptr[0]); + + setOutputLevel(); + return true; +} + +bool TownsPC98_MusicChannel::control_f1_presetOutputLevel(uint8 para) { + if (_fading) + return true; + + _totalLevel = _driver->_levelPresets[para]; + setOutputLevel(); + return true; +} + +bool TownsPC98_MusicChannel::control_f4_setOutputLevel(uint8 para) { + if (_fading) + return true; + + _totalLevel = para; + setOutputLevel(); return true; } bool TownsPC98_MusicChannel::control_fb_incOutLevel(uint8 para) { _dataPtr--; - if (_drv->_fading) + if (_fading) return true; uint8 val = (_totalLevel + 3); @@ -540,84 +557,103 @@ bool TownsPC98_MusicChannel::control_fb_incOutLevel(uint8 para) { bool TownsPC98_MusicChannel::control_fc_decOutLevel(uint8 para) { _dataPtr--; - if (_drv->_fading) + if (_fading) return true; int8 val = (int8)(_totalLevel - 3); if (val < 0) val = 0; - _totalLevel = (uint8) val; + _totalLevel = (uint8)val; setOutputLevel(); return true; } -bool TownsPC98_MusicChannel::control_fd_jump(uint8 para) { - uint8 *tmp = _drv->_trackPtr + READ_LE_UINT16(_dataPtr - 1); - _dataPtr = (tmp[1] == 1) ? tmp : (_dataPtr + 1); - return true; -} - -bool TownsPC98_MusicChannel::control_dummy(uint8 para) { - _dataPtr--; - return true; -} - bool TownsPC98_MusicChannel::control_ff_endOfTrack(uint8 para) { uint16 val = READ_LE_UINT16(--_dataPtr); if (val) { // loop - _dataPtr = _drv->_trackPtr + val; + _dataPtr = _driver->_trackPtr + val; return true; } else { // quit parsing for active channel --_dataPtr; _flags |= CHS_EOT; - _drv->_finishedChannelsFlag |= _idFlag; + _driver->_finishedChannelsFlag |= _idFlag; keyOff(); return false; } } -TownsPC98_MusicChannelSSG::TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs, - uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : - TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) { +const uint8 TownsPC98_MusicChannel::_controlEventSize[16] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x05, 0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02 }; + +#define CONTROL(x) _controlEvents.push_back(new ControlEvent(this, &TownsPC98_MusicChannelSSG::control_##x)) +TownsPC98_MusicChannelSSG::TownsPC98_MusicChannelSSG(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : +TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), _algorithm(0x80), + _ssgStartLvl(0), _ssgTl(0), _ssgStep(0), _ssgTicksLeft(0), _ssgTargetLvl(0) { + CONTROL(f0_setPatch); + CONTROL(f1_setTotalLevel); + CONTROL(f2_duration); + CONTROL(f3_pitchBend); + CONTROL(f4_setAlgorithm); + CONTROL(f5_tempo); + CONTROL(f6_repeatSection); + CONTROL(f7_setupVibrato); + CONTROL(f8_toggleVibrato); + CONTROL(f9_loadCustomPatch); + CONTROL(fa_writeReg); + CONTROL(fb_incOutLevel); + CONTROL(fc_decOutLevel); + CONTROL(fd_jump); + CONTROL(dummy); + CONTROL(ff_endOfTrack); + + if (!_envPatchData) { + _envPatchData = new uint8[256]; + memcpy(_envPatchData, _envData, 256); + } } +#undef CONTROL -void TownsPC98_MusicChannelSSG::init() { - _algorithm = 0x80; +TownsPC98_MusicChannelSSG::~TownsPC98_MusicChannelSSG() { + for (Common::Array<const ControlEvent*>::iterator i = _controlEvents.begin(); i != _controlEvents.end(); ++i) + delete *i; + delete[] _envPatchData; + _envPatchData = 0; +} + +void TownsPC98_MusicChannelSSG::reset() { + TownsPC98_MusicChannel::reset(); + _ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = 0; + + // Unlike the original we restore the default patch data. This fixes a bug + // where certain sound effects would bring each other out of tune (e.g. the + // dragon's fire in Darm's house in Kyra 1 would sound different each time + // you triggered another sfx by dropping an item etc.) + uint8 i = (10 + _regOffset) << 4; + const uint8 *src = _envData; + _envPatchData[i] = src[i]; + _envPatchData[i + 3] = src[i + 3]; + _envPatchData[i + 4] = src[i + 4]; + _envPatchData[i + 6] = src[i + 6]; + _envPatchData[i + 8] = src[i + 8]; + _envPatchData[i + 12] = src[i + 12]; +} -#define Control(x) &TownsPC98_MusicChannelSSG::control_##x - static const ControlEventFunc ctrlEventsSSG[] = { - Control(f0_setPatch), - Control(f1_setTotalLevel), - Control(f2_setKeyOffTime), - Control(f3_setFreqLSB), - Control(f4_setAlgorithm), - Control(f5_setTempo), - Control(f6_repeatSection), - Control(f7_setupVibrato), - Control(f8_toggleVibrato), - Control(f9_loadCustomPatch), - Control(fa_writeReg), - Control(fb_incOutLevel), - Control(fc_decOutLevel), - Control(fd_jump), - Control(dummy), - Control(ff_endOfTrack) - }; -#undef Control - - controlEvents = ctrlEventsSSG; +void TownsPC98_MusicChannelSSG::loadData(uint8 *data) { + _driver->preventRegisterWrite(_flags & CHS_PROTECT ? true : false); + TownsPC98_MusicChannel::loadData(data); + setOutputLevel(0); + _algorithm = 0x80; } void TownsPC98_MusicChannelSSG::processEvents() { if (_flags & CHS_EOT) return; - _drv->preventRegisterWrite(_flags & CHS_PROTECT ? true : false); + _driver->preventRegisterWrite(_flags & CHS_PROTECT ? true : false); - if (!_hold && _ticksLeft == _keyOffTime) + if (!_sustain && _ticksLeft == _duration) nextShape(); if (!--_ticksLeft) { @@ -637,23 +673,23 @@ void TownsPC98_MusicChannelSSG::processEvents() { if (cmd == 0x80) { nextShape(); - _hold = false; + _sustain = false; } else { - if (!_hold) { + if (!_sustain) { _instr &= 0xf0; - _ssgStep = _drv->_ssgPatches[_instr]; - _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; - _ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; - _ssgStartLvl = _drv->_ssgPatches[_instr + 3]; + _ssgStep = _envPatchData[_instr]; + _ssgTicksLeft = _envPatchData[_instr + 1] & 0x7f; + _ssgTargetLvl = _envPatchData[_instr + 2]; + _ssgStartLvl = _envPatchData[_instr + 3]; _flags = (_flags & ~CHS_SSGOFF) | CHS_KEYOFF; } keyOn(); - if (_hold == false || cmd != _frqBlockMSB) + if (_sustain == false || cmd != _frqBlockMSB) _flags |= CHS_RECALCFREQ; - _hold = (para & 0x80) ? true : false; + _sustain = (para & 0x80) ? true : false; _frqBlockMSB = cmd; } @@ -662,18 +698,18 @@ void TownsPC98_MusicChannelSSG::processEvents() { if (!(_flags & CHS_SSGOFF)) { if (--_ssgTicksLeft) { - if (!_drv->_fading) + if (!_driver->_fading) setOutputLevel(_ssgStartLvl); return; } - _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; + _ssgTicksLeft = _envPatchData[_instr + 1] & 0x7f; - if (_drv->_ssgPatches[_instr + 1] & 0x80) { + if (_envPatchData[_instr + 1] & 0x80) { uint8 t = _ssgStartLvl - _ssgStep; if (_ssgStep <= _ssgStartLvl && _ssgTargetLvl < t) { - if (!_drv->_fading) + if (!_driver->_fading) setOutputLevel(t); return; } @@ -682,7 +718,7 @@ void TownsPC98_MusicChannelSSG::processEvents() { uint8 p = (uint8)(t & 0xff); if (t < 256 && _ssgTargetLvl > p) { - if (!_drv->_fading) + if (!_driver->_fading) setOutputLevel(p); return; } @@ -691,9 +727,9 @@ void TownsPC98_MusicChannelSSG::processEvents() { setOutputLevel(_ssgTargetLvl); if (_ssgStartLvl && !(_instr & 8)) { _instr += 4; - _ssgStep = _drv->_ssgPatches[_instr]; - _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; - _ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; + _ssgStep = _envPatchData[_instr]; + _ssgTicksLeft = _envPatchData[_instr + 1] & 0x7f; + _ssgTargetLvl = _envPatchData[_instr + 2]; } else { _flags |= CHS_SSGOFF; setOutputLevel(0); @@ -702,16 +738,18 @@ void TownsPC98_MusicChannelSSG::processEvents() { } void TownsPC98_MusicChannelSSG::processFrequency() { + static const uint16 noteFrequencies[] = { 0xee8, 0xe12, 0xd48, 0xc89, 0xbd5, 0xb2b, 0xa8a, 0x9f3, 0x964, 0x8dd, 0x85e, 0x7e6 }; + if (_algorithm & 0x40) return; if (_flags & CHS_RECALCFREQ) { _block = _frqBlockMSB >> 4; - _frequency = READ_LE_UINT16(&_drv->_opnFreqTableSSG[(_frqBlockMSB & 0x0f) << 1]) + _frqLSB; + _frequency = noteFrequencies[_frqBlockMSB & 0x0f] + _pitchBend; uint16 f = _frequency >> _block; - _drv->writeReg(_part, _regOffset << 1, f & 0xff); - _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8); + writeReg(0, _regOffset << 1, f & 0xff); + writeReg(0, (_regOffset << 1) + 1, f >> 8); setupVibrato(); } @@ -721,21 +759,30 @@ void TownsPC98_MusicChannelSSG::processFrequency() { return; uint16 f = _frequency >> _block; - _drv->writeReg(_part, _regOffset << 1, f & 0xff); - _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8); + writeReg(0, _regOffset << 1, f & 0xff); + writeReg(0, (_regOffset << 1) + 1, f >> 8); } } -bool TownsPC98_MusicChannelSSG::processControlEvent(uint8 cmd) { - uint8 para = *_dataPtr++; - return (this->*controlEvents[cmd & 0x0f])(para); +void TownsPC98_MusicChannelSSG::protect() { + _flags |= CHS_PROTECT; } -void TownsPC98_MusicChannelSSG::nextShape() { - _instr = (_instr & 0xf0) + 0x0c; - _ssgStep = _drv->_ssgPatches[_instr]; - _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f; - _ssgTargetLvl = _drv->_ssgPatches[_instr + 2]; +void TownsPC98_MusicChannelSSG::restore() { + _flags &= ~CHS_PROTECT; + keyOn(); + writeReg(0, 8 + _regOffset, _ssgTl); + uint16 f = _frequency >> _block; + writeReg(0, _regOffset << 1, f & 0xff); + writeReg(0, (_regOffset << 1) + 1, f >> 8); +} + +void TownsPC98_MusicChannelSSG::fadeStep() { + _fading = true; + _totalLevel--; + if ((int8)_totalLevel < 0) + _totalLevel = 0; + setOutputLevel(_ssgStartLvl); } void TownsPC98_MusicChannelSSG::keyOn() { @@ -748,30 +795,17 @@ void TownsPC98_MusicChannelSSG::keyOn() { t = (t << (_regOffset + 1)) | (t >> (7 - _regOffset)); if (!(_algorithm & 0x80)) - _drv->writeReg(_part, 6, _algorithm & 0x7f); + writeReg(0, 6, _algorithm & 0x7f); - uint8 e = (_drv->_pc98a->readReg(0, 7) & c) | t; - _drv->writeReg(_part, 7, e); + uint8 e = (readReg(0, 7) & c) | t; + writeReg(0, 7, e); } -void TownsPC98_MusicChannelSSG::protect() { - _flags |= CHS_PROTECT; -} - -void TownsPC98_MusicChannelSSG::restore() { - _flags &= ~CHS_PROTECT; - keyOn(); - _drv->writeReg(_part, 8 + _regOffset, _ssgTl); - uint16 f = _frequency >> _block; - _drv->writeReg(_part, _regOffset << 1, f & 0xff); - _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8); -} - -void TownsPC98_MusicChannelSSG::loadData(uint8 *data) { - _drv->preventRegisterWrite(_flags & CHS_PROTECT ? true : false); - TownsPC98_MusicChannel::loadData(data); - setOutputLevel(0); - _algorithm = 0x80; +void TownsPC98_MusicChannelSSG::nextShape() { + _instr = (_instr & 0xf0) + 0x0c; + _ssgStep = _envPatchData[_instr]; + _ssgTicksLeft = _envPatchData[_instr + 1] & 0x7f; + _ssgTargetLvl = _envPatchData[_instr + 2]; } void TownsPC98_MusicChannelSSG::setOutputLevel(uint8 lvl) { @@ -780,31 +814,12 @@ void TownsPC98_MusicChannelSSG::setOutputLevel(uint8 lvl) { if (newTl == _ssgTl) return; _ssgTl = newTl; - _drv->writeReg(_part, 8 + _regOffset, _ssgTl); -} - -void TownsPC98_MusicChannelSSG::reset() { - TownsPC98_MusicChannel::reset(); - - // Unlike the original we restore the default patch data. This fixes a bug - // where certain sound effects would bring each other out of tune (e.g. the - // dragon's fire in Darm's house in Kyra 1 would sound different each time - // you triggered another sfx by dropping an item etc.) - uint8 i = (10 + _regOffset) << 4; - const uint8 *src = &_drv->_drvTables[156]; - _drv->_ssgPatches[i] = src[i]; - _drv->_ssgPatches[i + 3] = src[i + 3]; - _drv->_ssgPatches[i + 4] = src[i + 4]; - _drv->_ssgPatches[i + 6] = src[i + 6]; - _drv->_ssgPatches[i + 8] = src[i + 8]; - _drv->_ssgPatches[i + 12] = src[i + 12]; + writeReg(0, 8 + _regOffset, _ssgTl); } -void TownsPC98_MusicChannelSSG::fadeStep() { - _totalLevel--; - if ((int8)_totalLevel < 0) - _totalLevel = 0; - setOutputLevel(_ssgStartLvl); +bool TownsPC98_MusicChannelSSG::processControlEvent(uint8 cmd) { + uint8 para = *_dataPtr++; + return (*_controlEvents[cmd & 0x0f])(para); } bool TownsPC98_MusicChannelSSG::control_f0_setPatch(uint8 para) { @@ -816,7 +831,7 @@ bool TownsPC98_MusicChannelSSG::control_f0_setPatch(uint8 para) { } bool TownsPC98_MusicChannelSSG::control_f1_setTotalLevel(uint8 para) { - if (!_drv->_fading) + if (!_fading) _totalLevel = para; return true; } @@ -827,19 +842,19 @@ bool TownsPC98_MusicChannelSSG::control_f4_setAlgorithm(uint8 para) { } bool TownsPC98_MusicChannelSSG::control_f9_loadCustomPatch(uint8 para) { - _instr = (_drv->_sfxOffs + 10 + _regOffset) << 4; - _drv->_ssgPatches[_instr] = *_dataPtr++; - _drv->_ssgPatches[_instr + 3] = para; - _drv->_ssgPatches[_instr + 4] = *_dataPtr++; - _drv->_ssgPatches[_instr + 6] = *_dataPtr++; - _drv->_ssgPatches[_instr + 8] = *_dataPtr++; - _drv->_ssgPatches[_instr + 12] = *_dataPtr++; + _instr = (_driver->_sfxOffs + 10 + _regOffset) << 4; + _envPatchData[_instr] = *_dataPtr++; + _envPatchData[_instr + 3] = para; + _envPatchData[_instr + 4] = *_dataPtr++; + _envPatchData[_instr + 6] = *_dataPtr++; + _envPatchData[_instr + 8] = *_dataPtr++; + _envPatchData[_instr + 12] = *_dataPtr++; return true; } bool TownsPC98_MusicChannelSSG::control_fb_incOutLevel(uint8 para) { _dataPtr--; - if (_drv->_fading) + if (_fading) return true; _totalLevel--; @@ -851,7 +866,7 @@ bool TownsPC98_MusicChannelSSG::control_fb_incOutLevel(uint8 para) { bool TownsPC98_MusicChannelSSG::control_fc_decOutLevel(uint8 para) { _dataPtr--; - if (_drv->_fading) + if (_fading) return true; if (_totalLevel + 1 < 0x10) @@ -861,30 +876,68 @@ bool TownsPC98_MusicChannelSSG::control_fc_decOutLevel(uint8 para) { } bool TownsPC98_MusicChannelSSG::control_ff_endOfTrack(uint8 para) { - if (!_drv->_sfxOffs) { + if (!_driver->_sfxOffs) { uint16 val = READ_LE_UINT16(--_dataPtr); if (val) { // loop - _dataPtr = _drv->_trackPtr + val; + _dataPtr = _driver->_trackPtr + val; return true; } else { // stop parsing - if (!_drv->_fading) + if (!_driver->_fading) setOutputLevel(0); --_dataPtr; _flags |= CHS_EOT; - _drv->_finishedSSGFlag |= _idFlag; + _driver->_finishedSSGFlag |= _idFlag; } } else { // end of sfx track - restore ssg music channel _flags |= CHS_EOT; - _drv->_finishedSfxFlag |= _idFlag; - _drv->_ssgChannels[_chanNum]->restore(); + _driver->_finishedSfxFlag |= _idFlag; + _driver->_ssgChannels[_chanNum]->restore(); } return false; } +uint8 *TownsPC98_MusicChannelSSG::_envPatchData = 0; + +const uint8 TownsPC98_MusicChannelSSG::_envData[256] = { + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, + 0x04, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, + 0x0A, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0x01, 0x00, + 0xFF, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, + 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x64, 0x01, 0xFF, 0x64, 0xFF, 0x81, 0xFF, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + + 0x02, 0x01, 0xFF, 0x28, 0xFF, 0x81, 0xF0, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xC8, 0x00, + 0x01, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0x78, 0x5F, 0x81, 0xA0, 0x00, + 0x05, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00 +}; + void TownsPC98_SfxChannel::loadData(uint8 *data) { _flags = CHS_ALLOFF; _ticksLeft = 1; @@ -904,59 +957,55 @@ void TownsPC98_SfxChannel::loadData(uint8 *data) { tmp[0] = tmp[1]; tmp += 4; } else { - tmp += _drv->_opnFxCmdLen[cmd - 240]; + tmp += _controlEventSize[cmd - 240]; } } } void TownsPC98_SfxChannel::reset() { TownsPC98_MusicChannel::reset(); + _ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = 0; // Unlike the original we restore the default patch data. This fixes a bug // where certain sound effects would bring each other out of tune (e.g. the // dragon's fire in Darm's house in Kyra 1 would sound different each time // you triggered another sfx by dropping an item etc.) uint8 i = (13 + _regOffset) << 4; - const uint8 *src = &_drv->_drvTables[156]; - _drv->_ssgPatches[i] = src[i]; - _drv->_ssgPatches[i + 3] = src[i + 3]; - _drv->_ssgPatches[i + 4] = src[i + 4]; - _drv->_ssgPatches[i + 6] = src[i + 6]; - _drv->_ssgPatches[i + 8] = src[i + 8]; - _drv->_ssgPatches[i + 12] = src[i + 12]; + const uint8 *src = _envData; + _envPatchData[i] = src[i]; + _envPatchData[i + 3] = src[i + 3]; + _envPatchData[i + 4] = src[i + 4]; + _envPatchData[i + 6] = src[i + 6]; + _envPatchData[i + 8] = src[i + 8]; + _envPatchData[i + 12] = src[i + 12]; } #ifndef DISABLE_PC98_RHYTHM_CHANNEL -TownsPC98_MusicChannelPCM::TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs, - uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : - TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) { -} - -void TownsPC98_MusicChannelPCM::init() { - _algorithm = 0x80; - -#define Control(x) &TownsPC98_MusicChannelPCM::control_##x - static const ControlEventFunc ctrlEventsPCM[] = { - Control(dummy), - Control(f1_prcStart), - Control(dummy), - Control(dummy), - Control(dummy), - Control(dummy), - Control(f6_repeatSection), - Control(dummy), - Control(dummy), - Control(dummy), - Control(fa_writeReg), - Control(dummy), - Control(dummy), - Control(dummy), - Control(dummy), - Control(ff_endOfTrack) - }; -#undef Control - - controlEvents = ctrlEventsPCM; +#define CONTROL(x) _controlEvents.push_back(new ControlEvent(this, &TownsPC98_MusicChannelPCM::control_##x)) +TownsPC98_MusicChannelPCM::TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : +TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), _algorithm(0x80) { + CONTROL(dummy); + CONTROL(f1_prcStart); + CONTROL(dummy); + CONTROL(dummy); + CONTROL(dummy); + CONTROL(dummy); + CONTROL(f6_repeatSection); + CONTROL(dummy); + CONTROL(dummy); + CONTROL(dummy); + CONTROL(fa_writeReg); + CONTROL(dummy); + CONTROL(dummy); + CONTROL(dummy); + CONTROL(dummy); + CONTROL(ff_endOfTrack); +} +#undef CONTROL + +TownsPC98_MusicChannelPCM::~TownsPC98_MusicChannelPCM() { + for (Common::Array<const ControlEvent*>::iterator i = _controlEvents.begin(); i != _controlEvents.end(); ++i) + delete *i; } void TownsPC98_MusicChannelPCM::loadData(uint8 *data) { @@ -981,7 +1030,7 @@ void TownsPC98_MusicChannelPCM::processEvents() { if (cmd == 0x80) { loop = false; } else if (cmd < 0xf0) { - _drv->writeReg(_part, 0x10, cmd); + writeReg(0, 0x10, cmd); } else if (!processControlEvent(cmd)) { return; } @@ -992,12 +1041,12 @@ void TownsPC98_MusicChannelPCM::processEvents() { bool TownsPC98_MusicChannelPCM::processControlEvent(uint8 cmd) { uint8 para = *_dataPtr++; - return (this->*controlEvents[cmd & 0x0f])(para); + return (*_controlEvents[cmd & 0x0f])(para); } bool TownsPC98_MusicChannelPCM::control_f1_prcStart(uint8 para) { _totalLevel = para; - _drv->writeReg(_part, 0x11, para); + writeReg(0, 0x11, para); return true; } @@ -1005,13 +1054,13 @@ bool TownsPC98_MusicChannelPCM::control_ff_endOfTrack(uint8 para) { uint16 val = READ_LE_UINT16(--_dataPtr); if (val) { // loop - _dataPtr = _drv->_trackPtr + val; + _dataPtr = _driver->_trackPtr + val; return true; } else { // quit parsing for active channel --_dataPtr; _flags |= CHS_EOT; - _drv->_finishedRhythmFlag |= _idFlag; + _driver->_finishedRhythmFlag |= _idFlag; return false; } } @@ -1022,12 +1071,8 @@ TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type) #ifndef DISABLE_PC98_RHYTHM_CHANNEL _rhythmChannel(0), #endif - _trackPtr(0), _sfxData(0), _sfxOffs(0), _ssgPatches(0), - _patches(0), _sfxBuffer(0), _musicBuffer(0), - - _opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 108), _opnFreqTableSSG(_drvTables + 132), - _opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == kTypeTowns ? 52 : 84)), - + _sfxData(0), _sfxOffs(0), _patchData(0), _sfxBuffer(0), _musicBuffer(0), _trackPtr(0), + _levelPresets(type == kTypeTowns ? _levelPresetFMTOWNS : _levelPresetPC98), _updateChannelsFlag(type == kType26 ? 0x07 : 0x3F), _finishedChannelsFlag(0), _updateSSGFlag(type == kTypeTowns ? 0x00 : 0x07), _finishedSSGFlag(0), _updateRhythmFlag(type == kType86 ? @@ -1069,8 +1114,6 @@ TownsPC98_AudioDriver::~TownsPC98_AudioDriver() { #ifndef DISABLE_PC98_RHYTHM_CHANNEL delete _rhythmChannel; #endif - - delete[] _ssgPatches; } bool TownsPC98_AudioDriver::init() { @@ -1087,37 +1130,29 @@ bool TownsPC98_AudioDriver::init() { _channels = new TownsPC98_MusicChannel *[_numChanFM]; for (int i = 0; i < _numChanFM; i++) { int ii = i * 6; - _channels[i] = new TownsPC98_MusicChannel(this, _drvTables[ii], _drvTables[ii + 1], - _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]); - _channels[i]->init(); + _channels[i] = new TownsPC98_MusicChannel(this, _channelPreset[ii], _channelPreset[ii + 1], + _channelPreset[ii + 2], _channelPreset[ii + 3], _channelPreset[ii + 4], _channelPreset[ii + 5]); } if (_numChanSSG) { - _ssgPatches = new uint8[256]; - memcpy(_ssgPatches, _drvTables + 156, 256); - _ssgChannels = new TownsPC98_MusicChannelSSG *[_numChanSSG]; for (int i = 0; i < _numChanSSG; i++) { int ii = i * 6; - _ssgChannels[i] = new TownsPC98_MusicChannelSSG(this, _drvTables[ii], _drvTables[ii + 1], - _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]); - _ssgChannels[i]->init(); + _ssgChannels[i] = new TownsPC98_MusicChannelSSG(this, _channelPreset[ii], _channelPreset[ii + 1], + _channelPreset[ii + 2], _channelPreset[ii + 3], _channelPreset[ii + 4], _channelPreset[ii + 5]); } _sfxChannels = new TownsPC98_SfxChannel *[2]; for (int i = 0; i < 2; i++) { int ii = (i + 1) * 6; - _sfxChannels[i] = new TownsPC98_SfxChannel(this, _drvTables[ii], _drvTables[ii + 1], - _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]); - _sfxChannels[i]->init(); + _sfxChannels[i] = new TownsPC98_SfxChannel(this, _channelPreset[ii], _channelPreset[ii + 1], + _channelPreset[ii + 2], _channelPreset[ii + 3], _channelPreset[ii + 4], _channelPreset[ii + 5]); } } #ifndef DISABLE_PC98_RHYTHM_CHANNEL - if (_numChanRHY) { + if (_numChanRHY) _rhythmChannel = new TownsPC98_MusicChannelPCM(this, 0, 0, 0, 0, 0, 1); - _rhythmChannel->init(); - } #endif setMusicTempo(84); @@ -1142,7 +1177,7 @@ void TownsPC98_AudioDriver::loadMusicData(uint8 *data, bool loadPaused) { reset(); PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); - uint8 *src_a = _trackPtr = _musicBuffer = data; + const uint8 *src_a = _trackPtr = _musicBuffer = data; for (uint8 i = 0; i < 3; i++) { _channels[i]->loadData(data + READ_LE_UINT16(src_a)); @@ -1168,7 +1203,7 @@ void TownsPC98_AudioDriver::loadMusicData(uint8 *data, bool loadPaused) { preventRegisterWrite(false); - _patches = src_a + 4; + _patchData = src_a + 4; _finishedChannelsFlag = _finishedSSGFlag = _finishedRhythmFlag = 0; _musicPlaying = !loadPaused; @@ -1203,7 +1238,7 @@ void TownsPC98_AudioDriver::reset() { _musicPlaying = false; _sfxPlaying = false; - _fading = false; + _fading = 0; _looping = 0; _musicTickCounter = 0; _sfxData = 0; @@ -1218,8 +1253,6 @@ void TownsPC98_AudioDriver::reset() { if (_numChanSSG) { for (int i = 0; i < 2; i++) _sfxChannels[i]->reset(); - - memcpy(_ssgPatches, _drvTables + 156, 256); } #ifndef DISABLE_PC98_RHYTHM_CHANNEL @@ -1264,11 +1297,11 @@ void TownsPC98_AudioDriver::cont() { _musicPlaying = true; } -bool TownsPC98_AudioDriver::looping() { +bool TownsPC98_AudioDriver::looping() const { return _looping == _updateChannelsFlag ? true : false; } -bool TownsPC98_AudioDriver::musicPlaying() { +bool TownsPC98_AudioDriver::musicPlaying() const { return _musicPlaying; } @@ -1280,6 +1313,10 @@ void TownsPC98_AudioDriver::setSoundEffectVolume(int volume) { _pc98a->setSoundEffectVolume(volume); } +uint8 TownsPC98_AudioDriver::readReg(uint8 part, uint8 reg) { + return _pc98a->readReg(part, reg); +} + void TownsPC98_AudioDriver::writeReg(uint8 part, uint8 reg, uint8 val) { if (!_regWriteProtect) _pc98a->writeReg(part, reg, val); @@ -1377,76 +1414,24 @@ void TownsPC98_AudioDriver::setSfxTempo(uint16 tempo) { writeReg(0, 0x25, tempo >> 8); writeReg(0, 0x27, 0x33); } -const uint8 TownsPC98_AudioDriver::_drvTables[] = { - // channel presets + +const uint8 TownsPC98_AudioDriver::_channelPreset[36] = { 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x01, 0x80, 0x01, 0x01, 0x00, 0x02, 0x02, 0x80, 0x02, 0x02, 0x00, 0x04, 0x00, 0x80, 0x03, 0x04, 0x01, 0x08, 0x01, 0x80, 0x04, 0x05, 0x01, 0x10, - 0x02, 0x80, 0x05, 0x06, 0x01, 0x20, - - // control event size - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x05, - 0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02, + 0x02, 0x80, 0x05, 0x06, 0x01, 0x20 +}; - // fmt level presets +const uint8 TownsPC98_AudioDriver::_levelPresetFMTOWNS[24] = { 0x54, 0x50, 0x4C, 0x48, 0x44, 0x40, 0x3C, 0x38, 0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18, - 0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90, - - // carriers - 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F, + 0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90 +}; - // pc98 level presets +const uint8 TownsPC98_AudioDriver::_levelPresetPC98[24] = { 0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25, 0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10, - 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90, - - // frequencies - 0x6A, 0x02, 0x8F, 0x02, 0xB6, 0x02, 0xDF, 0x02, - 0x0B, 0x03, 0x39, 0x03, 0x6A, 0x03, 0x9E, 0x03, - 0xD5, 0x03, 0x10, 0x04, 0x4E, 0x04, 0x8F, 0x04, - - // ssg frequencies - 0xE8, 0x0E, 0x12, 0x0E, 0x48, 0x0D, 0x89, 0x0C, - 0xD5, 0x0B, 0x2B, 0x0B, 0x8A, 0x0A, 0xF3, 0x09, - 0x64, 0x09, 0xDD, 0x08, 0x5E, 0x08, 0xE6, 0x07, - - // ssg patch data - 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, - 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00, - 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00, - 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, - 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, - 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, - 0x04, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00, - 0x0A, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0x01, 0x00, - 0xFF, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, - 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x00, - 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - 0x64, 0x01, 0xFF, 0x64, 0xFF, 0x81, 0xFF, 0x00, - 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - - 0x02, 0x01, 0xFF, 0x28, 0xFF, 0x81, 0xF0, 0x00, - 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xC8, 0x00, - 0x01, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0x78, 0x5F, 0x81, 0xA0, 0x00, - 0x05, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, - 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, - 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00, - 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00 + 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90 }; - -#undef EUPHONY_FADEOUT_TICKS diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h index 0b9edcfd58..448bb2a8e1 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_driver.h @@ -53,13 +53,14 @@ public: void pause(); void cont(); - bool looping(); - bool musicPlaying(); + bool looping() const; + bool musicPlaying() const; void setMusicVolume(int volume); void setSoundEffectVolume(int volume); private: + uint8 readReg(uint8 part, uint8 reg); void writeReg(uint8 part, uint8 reg, uint8 val); void preventRegisterWrite(bool prevent); @@ -78,17 +79,9 @@ private: TownsPC98_MusicChannelPCM *_rhythmChannel; #endif - const uint8 *_opnCarrier; - const uint8 *_opnFreqTable; - const uint8 *_opnFreqTableSSG; - const uint8 *_opnFxCmdLen; - const uint8 *_opnLvlPresets; - uint8 *_musicBuffer; uint8 *_sfxBuffer; - uint8 *_trackPtr; - uint8 *_patches; - uint8 *_ssgPatches; + const uint8 *_patchData; uint8 _updateChannelsFlag; uint8 _updateSSGFlag; @@ -109,14 +102,19 @@ private: uint8 *_sfxData; uint16 _sfxOffsets[2]; + uint8 *_trackPtr; bool _regWriteProtect; + PC98AudioCore *_pc98a; const int _numChanFM; const int _numChanSSG; const int _numChanRHY; - static const uint8 _drvTables[]; + static const uint8 _channelPreset[36]; + static const uint8 _levelPresetFMTOWNS[24]; + static const uint8 _levelPresetPC98[24]; + const uint8 *_levelPresets; bool _ready; }; |