aboutsummaryrefslogtreecommitdiff
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_pc98_driver.cpp1003
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_pc98_driver.h22
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;
};