aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/sound_towns.cpp
diff options
context:
space:
mode:
authorFlorian Kagerer2008-08-16 12:38:37 +0000
committerFlorian Kagerer2008-08-16 12:38:37 +0000
commit807bfe50796edd683d0aa026adc2cfbca7c490dd (patch)
tree8c47ffb8a7b2359412f74fcae6d4ab672b36dbfb /engines/kyra/sound_towns.cpp
parent49aff727e282edf3e07dcc20a8516ad60fdf7c11 (diff)
downloadscummvm-rg350-807bfe50796edd683d0aa026adc2cfbca7c490dd.tar.gz
scummvm-rg350-807bfe50796edd683d0aa026adc2cfbca7c490dd.tar.bz2
scummvm-rg350-807bfe50796edd683d0aa026adc2cfbca7c490dd.zip
- KYRA: FM-Towns/PC-98 Audio: fixed several bugs and got rid of some floating point arithmetic (or at least replaced some doubles with floats)
- Improved support for PC-98 music: HOF tracks seem to be fine, KYRA should be okay, too (I can't compare with an emulator since I am missing the floppy boot disk with the executable) - There are still tempo issues. Some tracks play way too slow, others way too fast - PC-98 sound effects in Kyra 1 don't work (I would need the floppy disk with the executable for that) svn-id: r33938
Diffstat (limited to 'engines/kyra/sound_towns.cpp')
-rw-r--r--engines/kyra/sound_towns.cpp1320
1 files changed, 1006 insertions, 314 deletions
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index ec1962a58f..d4c6afa4f6 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -427,7 +427,7 @@ void Towns_EuphonyPcmChannel::nextTick(int32 *outbuf, int buflen) {
return;
}
- float phaseStep = SoundTowns::semitoneAndSampleRate_to_sampleStep(_note, _voice->_snd[_current]->keyNote -
+ float phaseStep = SoundTowns::calculatePhaseStep(_note, _voice->_snd[_current]->keyNote -
_voice->_env[_current]->rootKeyOffset, _voice->_snd[_current]->samplingRate, _rate, _frequencyOffs);
int32 looplength = _voice->_snd[_current]->loopLength;
@@ -1085,7 +1085,7 @@ void Towns_EuphonyTrackQueue::initDriver() {
class TownsPC98_OpnOperator {
public:
- TownsPC98_OpnOperator(double rate, const uint8 *rateTable,
+ TownsPC98_OpnOperator(float rate, const uint8 *rateTable,
const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,
const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable);
~TownsPC98_OpnOperator() {}
@@ -1095,7 +1095,7 @@ public:
void frequency(int freq);
void updatePhaseIncrement();
void recalculateRates();
- void generateOutput(int phasebuf, int *_feedbuf, int &out);
+ void generateOutput(int phasebuf, int *feedbuf, int &out);
void feedbackLevel(int32 level) {_feedbackLevel = level ? level + 6 : 0; }
void detune(int value) { _detn = &_detnTbl[value << 5]; }
@@ -1137,8 +1137,8 @@ protected:
const int32 *_tLvlTbl;
const int32 *_detnTbl;
- const double _tickLength;
- double _tick;
+ const int _tickLength;
+ int _tick;
int32 _currentLevel;
struct EvpState {
@@ -1147,11 +1147,11 @@ protected:
} fs_a, fs_d, fs_s, fs_r;
};
-TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, const uint8 *rateTable,
+TownsPC98_OpnOperator::TownsPC98_OpnOperator(float rate, const uint8 *rateTable,
const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,
const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) :
_rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable),
- _sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(rate * 65536.0),
+ _sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength((int)(rate * 65536.0f)),
_specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0),
_phase(0), _state(s_ready) {
@@ -1172,6 +1172,7 @@ void TownsPC98_OpnOperator::frequency(int freq) {
uint8 block = (freq >> 11);
uint16 pos = (freq & 0x7ff);
uint8 c = pos >> 7;
+
_kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6 ));
_frequency = _fTbl[pos << 1] >> (7 - block);
}
@@ -1204,7 +1205,7 @@ void TownsPC98_OpnOperator::recalculateRates() {
fs_r.shift = _rshiftTbl[r + k];
}
-void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out) {
+void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *feed, int &out) {
if (_state == s_ready)
return;
@@ -1216,32 +1217,32 @@ void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out
int32 levelIncrement = 0;
uint32 targetTime = 0;
int32 targetLevel = 0;
- EnvelopeState next_state = s_ready;
+ EnvelopeState nextState = s_ready;
switch (_state) {
case s_ready:
return;
case s_attacking:
- next_state = s_decaying;
+ nextState = s_decaying;
targetTime = (1 << fs_a.shift) - 1;
targetLevel = 0;
levelIncrement = (~_currentLevel * _adTbl[fs_a.rate + ((_tickCount >> fs_a.shift) & 7)]) >> 4;
break;
case s_decaying:
targetTime = (1 << fs_d.shift) - 1;
- next_state = s_sustaining;
+ nextState = s_sustaining;
targetLevel = _sustainLevel;
levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)];
break;
case s_sustaining:
targetTime = (1 << fs_s.shift) - 1;
- next_state = s_ready;
+ nextState = s_sustaining;
targetLevel = 1023;
levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)];
break;
case s_releasing:
targetTime = (1 << fs_r.shift) - 1;
- next_state = s_ready;
+ nextState = s_ready;
targetLevel = 1023;
levelIncrement = _adTbl[fs_r.rate + ((_tickCount >> fs_r.shift) & 7)];
break;
@@ -1249,11 +1250,10 @@ void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out
if (!(_tickCount & targetTime)) {
_currentLevel += levelIncrement;
- if ((!targetLevel && _currentLevel <= targetLevel) || (targetLevel && _currentLevel >= targetLevel)) {
+ if ((_state == s_attacking && _currentLevel <= targetLevel) || (_state != s_attacking && _currentLevel >= targetLevel)) {
if (_state != s_decaying)
_currentLevel = targetLevel;
- if (_state != s_sustaining)
- _state = next_state;
+ _state = nextState;
}
}
}
@@ -1264,10 +1264,10 @@ void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out
int *i = &outp, *o = &outp;
int phaseShift = 0;
- if (_feedbuf) {
- o = &_feedbuf[0];
- i = &_feedbuf[1];
- phaseShift = _feedbackLevel ? ((_feedbuf[0] + _feedbuf[1]) << _feedbackLevel) : 0;
+ if (feed) {
+ o = &feed[0];
+ i = &feed[1];
+ phaseShift = _feedbackLevel ? ((*o + *i) << _feedbackLevel) : 0;
if (phasebuf == -1)
*i = 0;
*o = *i;
@@ -1332,25 +1332,24 @@ public:
virtual ~TownsPC98_OpnChannel();
virtual void init();
- typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para);
-
typedef enum channelState {
CHS_RECALCFREQ = 0x01,
CHS_KEYOFF = 0x02,
- CHS_SSG = 0x04,
+ CHS_SSGOFF = 0x04,
CHS_PITCHWHEELOFF = 0x08,
CHS_ALL_BUT_EOT = 0x0f,
+ CHS_PROTECT = 0x40,
CHS_EOT = 0x80
} ChannelState;
virtual void loadData(uint8 *data);
virtual void processEvents();
virtual void processFrequency();
- bool processControlEvent(uint8 cmd);
+ virtual bool processControlEvent(uint8 cmd);
void writeReg(uint8 regAdress, uint8 value);
virtual void keyOn();
- virtual void keyOff();
+ void keyOff();
void setOutputLevel();
void fadeStep();
@@ -1361,11 +1360,14 @@ public:
bool _enableLeft;
bool _enableRight;
- bool _updateEnvelopes;
+ bool _updateEnvelopeParameters;
const uint8 _idFlag;
int _feedbuf[3];
protected:
+ void setupPitchWheel();
+ bool processPitchWheel();
+
bool control_dummy(uint8 para);
bool control_f0_setPatch(uint8 para);
bool control_f1_presetOutputLevel(uint8 para);
@@ -1377,40 +1379,36 @@ protected:
bool control_f7_setupPitchWheel(uint8 para);
bool control_f8_togglePitchWheel(uint8 para);
bool control_fa_writeReg(uint8 para);
- bool control_fb_incOutLevel(uint8 para);
- bool control_fc_decOutLevel(uint8 para);
+ virtual bool control_fb_incOutLevel(uint8 para);
+ virtual bool control_fc_decOutLevel(uint8 para);
bool control_fd_jump(uint8 para);
- bool control_ff_endOfTrack(uint8 para);
-
- bool control_f0_setPatchSSG(uint8 para);
- bool control_f1_setTotalLevel(uint8 para);
- bool control_f4_setAlgorithm(uint8 para);
- bool control_f9_unkSSG(uint8 para);
- bool control_fb_incOutLevelSSG(uint8 para);
- bool control_fc_decOutLevelSSG(uint8 para);
- bool control_ff_endOfTrackSSG(uint8 para);
+ virtual bool control_ff_endOfTrack(uint8 para);
uint8 _ticksLeft;
uint8 _algorithm;
- uint8 _instrID;
+ uint8 _instr;
uint8 _totalLevel;
uint8 _frqBlockMSB;
int8 _frqLSB;
uint8 _keyOffTime;
- bool _protect;
+ bool _hold;
uint8 *_dataPtr;
- uint8 _ptchWhlInitDelayLo;
uint8 _ptchWhlInitDelayHi;
+ uint8 _ptchWhlInitDelayLo;
int16 _ptchWhlModInitVal;
uint8 _ptchWhlDuration;
uint8 _ptchWhlCurDelay;
int16 _ptchWhlModCurVal;
uint8 _ptchWhlDurLeft;
- uint16 frequency;
+ uint16 _frequency;
+ uint8 _block;
uint8 _regOffset;
uint8 _flags;
- uint8 _ssg1;
- uint8 _ssg2;
+ uint8 _ssgTl;
+ uint8 _ssgStep;
+ uint8 _ssgTicksLeft;
+ uint8 _ssgTargetLvl;
+ uint8 _ssgStartLvl;
const uint8 _chanNum;
const uint8 _keyNum;
@@ -1420,6 +1418,7 @@ protected:
TownsPC98_OpnOperator **_opr;
uint16 _frqTemp;
+ typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para);
const ControlEventFunc *controlEvents;
};
@@ -1427,24 +1426,129 @@ class TownsPC98_OpnChannelSSG : public TownsPC98_OpnChannel {
public:
TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,
uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
- ~TownsPC98_OpnChannelSSG() {}
+ virtual ~TownsPC98_OpnChannelSSG() {}
void init();
+ virtual void loadData(uint8 *data);
void processEvents();
void processFrequency();
+ bool processControlEvent(uint8 cmd);
void keyOn();
- void keyOff();
+ void nextShape();
+
+ void protect();
+ void restore();
+
+ void fadeStep();
+
+protected:
+ void setOutputLevel(uint8 lvl);
+
+ bool control_f0_setInstr(uint8 para);
+ bool control_f1_setTotalLevel(uint8 para);
+ bool control_f4_setAlgorithm(uint8 para);
+ bool control_f9_loadCustomPatch(uint8 para);
+ bool control_fb_incOutLevel(uint8 para);
+ bool control_fc_decOutLevel(uint8 para);
+ bool control_ff_endOfTrack(uint8 para);
+
+ typedef bool (TownsPC98_OpnChannelSSG::*ControlEventFunc)(uint8 para);
+ const ControlEventFunc *controlEvents;
+};
+
+class TownsPC98_OpnSfxChannel : public TownsPC98_OpnChannelSSG {
+public:
+ TownsPC98_OpnSfxChannel(TownsPC98_OpnDriver *driver, uint8 regOffs,
+ uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
+ TownsPC98_OpnChannelSSG(driver, regOffs, flgs, num, key, prt, id) {}
+ ~TownsPC98_OpnSfxChannel() {}
+
void loadData(uint8 *data);
+};
+
+class TownsPC98_OpnChannelPCM : public TownsPC98_OpnChannel {
+public:
+ TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs,
+ uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
+ ~TownsPC98_OpnChannelPCM() {}
+ void init();
+
+ void loadData(uint8 *data);
+ void processEvents();
+ bool processControlEvent(uint8 cmd);
+
+ void fadeStep();
private:
- void opn_SSG_UNK(uint8 a);
+ bool control_f1_pcmStart(uint8 para);
+ bool control_ff_endOfTrack(uint8 para);
+
+ typedef bool (TownsPC98_OpnChannelPCM::*ControlEventFunc)(uint8 para);
+ const ControlEventFunc *controlEvents;
};
+class TownsPC98_SSG : public Audio::AudioStream {
+public:
+ TownsPC98_SSG(TownsPC98_OpnDriver *driver, Audio::Mixer *mixer, float rate);
+ ~TownsPC98_SSG();
+
+ void init(const int *rsTable, const int *rseTable);
+ void reset();
+ uint8 readReg(uint8 address);
+ void writeReg(uint8 address, uint8 value, bool force = false);
+
+ // AudioStream interface
+ int inline readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _mixer->getOutputRate(); }
+
+private:
+ void updatesRegs();
+ void nextTick(int16 *buffer, uint32 bufferSize);
+
+ uint8 _reg[16];
+ uint8 _updateRequestBuf[32];
+ int _updateRequest;
+ uint8 *_regIndex;
+ int _rand;
+
+ int8 _evpTimer;
+ uint32 _pReslt;
+ uint8 _attack;
+
+ bool _evpUpdate, _cont;
+
+ int _evpUpdateCnt;
+ uint8 _outN;
+ int _nTick;
+
+ int32 *_tlTable;
+ int32 *_tleTable;
+
+ const float _rate;
+ const int _tickLength;
+ int _timer;
+
+ struct Channel {
+ int tick;
+ uint8 smp;
+ uint8 out;
+ } _channels[3];
+
+ TownsPC98_OpnDriver *_drv;
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _soundHandle;
+
+ bool _ready;
+};
class TownsPC98_OpnDriver : public Audio::AudioStream {
friend class TownsPC98_OpnChannel;
friend class TownsPC98_OpnChannelSSG;
+friend class TownsPC98_OpnSfxChannel;
+friend class TownsPC98_OpnChannelPCM;
public:
enum OpnType {
OD_TOWNS,
@@ -1456,12 +1560,13 @@ public:
~TownsPC98_OpnDriver();
bool init();
- void loadData(uint8 *data, bool loadPaused = false);
+ void loadMusicData(uint8 *data, bool loadPaused = false);
+ void loadSoundEffectData(uint8 *data, uint8 trackNum);
void reset();
void fadeOut();
- void pause() { _playing = false; }
- void cont() { _playing = true; }
+ void pause() { _musicPlaying = false; }
+ void cont() { _musicPlaying = true; }
void callback();
void nextTick(int16 *buffer, uint32 bufferSize);
@@ -1479,8 +1584,11 @@ protected:
TownsPC98_OpnChannel **_channels;
TownsPC98_OpnChannelSSG **_ssgChannels;
- //TownsPC98_OpnChannel *_adpcmChannel;
+ TownsPC98_OpnSfxChannel **_sfxChannels;
+ TownsPC98_OpnChannelPCM *_pcmChannel;
+ TownsPC98_SSG *_ssg;
+ void startSoundEffect();
void setTempo(uint8 tempo);
void lock() { _mutex.lock(); }
@@ -1492,6 +1600,7 @@ protected:
const uint8 *_opnCarrier;
const uint8 *_opnFreqTable;
+ const uint8 *_opnFreqTableSSG;
const uint8 *_opnFxCmdLen;
const uint8 *_opnLvlPresets;
@@ -1503,20 +1612,33 @@ protected:
int32 *_oprLevelOut;
int32 *_oprDetune;
- uint8 *_trackData;
+ uint8 *_musicBuffer;
+ uint8 *_sfxBuffer;
+ uint8 *_trackPtr;
uint8 *_patches;
+ uint8 *_ssgPatches;
uint8 _cbCounter;
uint8 _updateChannelsFlag;
+ uint8 _updateSSGFlag;
+ uint8 _updatePCMFlag;
uint8 _finishedChannelsFlag;
+ uint8 _finishedSSGFlag;
+ uint8 _finishedPCMFlag;
+
uint16 _tempo;
- bool _playing;
+ bool _musicPlaying;
+ bool _sfxPlaying;
bool _fading;
uint8 _looping;
uint32 _tickCounter;
- bool _updateEnvelopes;
- int _ssgFlag;
+ bool _updateEnvelopeParameters;
+
+ bool _regProtectionFlag;
+ int _sfxOffs;
+ uint8 *_sfxData;
+ uint16 _sfxOffsets[2];
int32 _samplesTillCallback;
int32 _samplesTillCallbackRemainder;
@@ -1525,12 +1647,14 @@ protected:
const int _numChan;
const int _numSSG;
- const bool _hasADPCM;
+ const bool _hasPCM;
const bool _hasStereo;
- double _baserate;
static const uint8 _drvTables[];
static const uint32 _adtStat[];
+ static const int _ssgTables[];
+
+ const float _baserate;
bool _ready;
};
@@ -1538,14 +1662,15 @@ TownsPC98_OpnChannel::TownsPC98_OpnChannel(TownsPC98_OpnDriver *driver, uint8 re
uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key),
_part(prt), _idFlag(id) {
- _ticksLeft = _algorithm = _instrID = _totalLevel = _frqBlockMSB = _keyOffTime = _ssg1 = _ssg2 = 0;
- _ptchWhlInitDelayLo = _ptchWhlInitDelayHi = _ptchWhlDuration = _ptchWhlCurDelay = _ptchWhlDurLeft = 0;
+ _ticksLeft = _algorithm = _instr = _totalLevel = _frqBlockMSB = _keyOffTime = 0;
+ _ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = _block = 0;
+ _ptchWhlInitDelayHi = _ptchWhlInitDelayLo = _ptchWhlDuration = _ptchWhlCurDelay = _ptchWhlDurLeft = 0;
_frqLSB = 0;
- _protect = _updateEnvelopes = false;
+ _hold = _updateEnvelopeParameters = false;
_enableLeft = _enableRight = true;
_dataPtr = 0;
_ptchWhlModInitVal = _ptchWhlModCurVal = 0;
- frequency = _frqTemp = 0;
+ _frequency = _frqTemp = 0;
memset(&_feedbuf, 0, sizeof(int) * 3);
_opr = 0;
}
@@ -1558,8 +1683,7 @@ TownsPC98_OpnChannel::~TownsPC98_OpnChannel() {
}
}
-void TownsPC98_OpnChannel::init() {
-
+void TownsPC98_OpnChannel::init() {
_opr = new TownsPC98_OpnOperator*[4];
for (int i = 0; i < 4; i++)
_opr[i] = new TownsPC98_OpnOperator(_drv->_baserate, _drv->_oprRates, _drv->_oprRateshift,
@@ -1645,13 +1769,13 @@ void TownsPC98_OpnChannel::processEvents() {
if (_flags & CHS_EOT)
return;
- if (_protect == false && _ticksLeft == _keyOffTime)
+ if (_hold == false && _ticksLeft == _keyOffTime)
keyOff();
if (--_ticksLeft)
return;
- if (_protect == false)
+ if (_hold == false)
keyOff();
uint8 cmd = 0;
@@ -1669,14 +1793,14 @@ void TownsPC98_OpnChannel::processEvents() {
if (cmd == 0x80) {
keyOff();
- _protect = false;
+ _hold = false;
} else {
keyOn();
- if (_protect == false || cmd != _frqBlockMSB)
+ if (_hold == false || cmd != _frqBlockMSB)
_flags |= CHS_RECALCFREQ;
- _protect = (para & 0x80) ? true : false;
+ _hold = (para & 0x80) ? true : false;
_frqBlockMSB = cmd;
}
@@ -1687,35 +1811,46 @@ void TownsPC98_OpnChannel::processFrequency() {
if (_flags & CHS_RECALCFREQ) {
uint8 block = (_frqBlockMSB & 0x70) >> 1;
uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f];
- frequency = (bfreq + _frqLSB) | (block << 8);
-
- writeReg(_regOffset + 0xa4, (frequency >> 8));
- writeReg(_regOffset + 0xa0, (frequency & 0xff));
- _ptchWhlCurDelay = _ptchWhlInitDelayHi;
- if (_flags & CHS_KEYOFF) {
- _ptchWhlModCurVal = _ptchWhlModInitVal;
- _ptchWhlCurDelay += _ptchWhlInitDelayLo;
- }
+ _frequency = (bfreq + _frqLSB) | (block << 8);
+ writeReg(_regOffset + 0xa4, (_frequency >> 8));
+ writeReg(_regOffset + 0xa0, (_frequency & 0xff));
- _ptchWhlDurLeft = (_ptchWhlDuration >> 1);
- _flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
+ setupPitchWheel();
}
if (!(_flags & CHS_PITCHWHEELOFF)) {
- if (--_ptchWhlCurDelay)
+ if (!processPitchWheel())
return;
- _ptchWhlCurDelay = _ptchWhlInitDelayHi;
- frequency += _ptchWhlModCurVal;
- writeReg(_regOffset + 0xa4, (frequency >> 8));
- writeReg(_regOffset + 0xa0, (frequency & 0xff));
+ writeReg(_regOffset + 0xa4, (_frequency >> 8));
+ writeReg(_regOffset + 0xa0, (_frequency & 0xff));
+ }
+}
- if(!--_ptchWhlDurLeft) {
- _ptchWhlDurLeft = _ptchWhlDuration;
- _ptchWhlModCurVal = -_ptchWhlModCurVal;
- }
+void TownsPC98_OpnChannel::setupPitchWheel() {
+ _ptchWhlCurDelay = _ptchWhlInitDelayHi;
+ if (_flags & CHS_KEYOFF) {
+ _ptchWhlModCurVal = _ptchWhlModInitVal;
+ _ptchWhlCurDelay += _ptchWhlInitDelayLo;
}
+ _ptchWhlDurLeft = (_ptchWhlDuration >> 1);
+ _flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
+}
+
+bool TownsPC98_OpnChannel::processPitchWheel() {
+ if (--_ptchWhlCurDelay)
+ return false;
+
+ _ptchWhlCurDelay = _ptchWhlInitDelayHi;
+ _frequency += _ptchWhlModCurVal;
+
+ if(!--_ptchWhlDurLeft) {
+ _ptchWhlDurLeft = _ptchWhlDuration;
+ _ptchWhlModCurVal = -_ptchWhlModCurVal;
+ }
+
+ return true;
}
bool TownsPC98_OpnChannel::processControlEvent(uint8 cmd) {
@@ -1743,10 +1878,23 @@ void TownsPC98_OpnChannel::fadeStep() {
}
void TownsPC98_OpnChannel::reset() {
- for (int i = 0; i < 4; i++)
- _opr[i]->reset();
+ if (_opr) {
+ for (int i = 0; i < 4; i++)
+ _opr[i]->reset();
+ }
+
+ _block = 0;
+ _frequency = 0;
+ _hold = false;
+ _frqTemp = 0;
+ _ssgTl = 0;
+ _ssgStartLvl = 0;
+ _ssgTargetLvl = 0;
+ _ssgStep = 0;
+ _ssgTicksLeft = 0;
+ _totalLevel = 0;
- _updateEnvelopes = false;
+ _updateEnvelopeParameters = false;
_enableLeft = _enableRight = true;
memset(&_feedbuf, 0, sizeof(int) * 3);
}
@@ -1820,7 +1968,7 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,
};
if (_enableLeft) {
- int l = output + leftSample;
+ int l = output + (int) leftSample;
if (l > 32767)
l = 32767;
if (l < -32767)
@@ -1829,7 +1977,7 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,
}
if (_enableRight) {
- int r = output + rightSample;
+ int r = output + (int) rightSample;
if (r > 32767)
r = 32767;
if (r < -32767)
@@ -1839,19 +1987,29 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,
}
void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
+ if (_drv->_regProtectionFlag)
+ return;
+
uint8 h = regAdress & 0xf0;
uint8 l = (regAdress & 0x0f);
static const uint8 oprOrdr[] = { 0, 2, 1, 3 };
uint8 o = oprOrdr[(l - _regOffset) >> 2];
-
+
switch (h) {
case 0x00:
// ssg
- warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ if (_drv->_ssg)
+ _drv->_ssg->writeReg(regAdress, value);
break;
case 0x10:
- // adpcm
- warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ // pcm rhythm channel
+ if (!(regAdress -= 0x10) && !(value & 0x80)) {
+ static const char *names[] = { "bass drum", "snare drum", "top cymbal", "high hat", "tom tom", "rim shot" };
+ for (int i = 0; i < 6; i++)
+ if ((value >> i) & 1)
+ debugC(9, kDebugLevelMain | kDebugLevelSound, "TownsPC98_OpnDriver: TRYING TO USE UNSUPPORTED PCM INSTRUMENT: '%s'", names[i]);
+ }
+
break;
case 0x20:
if (l == 8) {
@@ -1884,7 +2042,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
// detune, multiple
_opr[o]->detune((value >> 4) & 7);
_opr[o]->multiple(value & 0x0f);
- _updateEnvelopes = true;
+ _updateEnvelopeParameters = true;
break;
case 0x40:
@@ -1896,7 +2054,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
// rate scaling, attack rate
_opr[o]->attackRate(value & 0x1f);
if (_opr[o]->scaleRate(value >> 6))
- _updateEnvelopes = true;
+ _updateEnvelopeParameters = true;
break;
case 0x60:
@@ -1904,7 +2062,6 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
_opr[o]->decayRate(value & 0x1f);
if (value & 0x80)
warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION (NOT SUPPORTED)");
-
break;
case 0x70:
@@ -1919,7 +2076,6 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
break;
case 0x90:
- // ssg
warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
break;
@@ -1928,7 +2084,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
l -= _regOffset;
if (l == 0) {
_frqTemp = (_frqTemp & 0xff00) | value;
- _updateEnvelopes = true;
+ _updateEnvelopeParameters = true;
for (int i = 0; i < 4; i++)
_opr[i]->frequency(_frqTemp);
} else if (l == 4) {
@@ -1970,7 +2126,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
}
bool TownsPC98_OpnChannel::control_f0_setPatch(uint8 para) {
- _instrID = para;
+ _instr = para;
uint8 reg = _regOffset + 0x80;
for (int i = 0; i < 4; i++) {
@@ -1979,7 +2135,7 @@ bool TownsPC98_OpnChannel::control_f0_setPatch(uint8 para) {
reg += 4;
}
- const uint8 *tptr = _drv->_patches + ((uint32)_instrID << 5);
+ const uint8 *tptr = _drv->_patches + ((uint32)_instr << 5);
reg = _regOffset + 0x30;
// write registers 0x30 to 0x8f
@@ -2043,7 +2199,7 @@ bool TownsPC98_OpnChannel::control_f6_repeatSection(uint8 para) {
if (*_dataPtr) {
// repeat section until counter has reached zero
- _dataPtr = _drv->_trackData + READ_LE_UINT16(_dataPtr + 2);
+ _dataPtr = _drv->_trackPtr + READ_LE_UINT16(_dataPtr + 2);
} else {
// reset counter, advance to next section
_dataPtr[0] = _dataPtr[1];
@@ -2053,8 +2209,8 @@ bool TownsPC98_OpnChannel::control_f6_repeatSection(uint8 para) {
}
bool TownsPC98_OpnChannel::control_f7_setupPitchWheel(uint8 para) {
- _ptchWhlInitDelayLo = _dataPtr[0];
- _ptchWhlInitDelayHi = para;
+ _ptchWhlInitDelayHi = _dataPtr[0];
+ _ptchWhlInitDelayLo = para;
_ptchWhlModInitVal = (int16) READ_LE_UINT16(_dataPtr + 1);
_ptchWhlDuration = _dataPtr[3];
_dataPtr += 4;
@@ -2113,7 +2269,7 @@ bool TownsPC98_OpnChannel::control_fc_decOutLevel(uint8 para) {
}
bool TownsPC98_OpnChannel::control_fd_jump(uint8 para) {
- uint8 *tmp = _drv->_trackData + READ_LE_UINT16(_dataPtr - 1);
+ uint8 *tmp = _drv->_trackPtr + READ_LE_UINT16(_dataPtr - 1);
_dataPtr = (tmp[1] == 1) ? tmp : ++_dataPtr;
return true;
}
@@ -2127,7 +2283,7 @@ bool TownsPC98_OpnChannel::control_ff_endOfTrack(uint8 para) {
uint16 val = READ_LE_UINT16(--_dataPtr);
if (val) {
// loop
- _dataPtr = _drv->_trackData + val;
+ _dataPtr = _drv->_trackPtr + val;
return true;
} else {
// quit parsing for active channel
@@ -2139,31 +2295,250 @@ bool TownsPC98_OpnChannel::control_ff_endOfTrack(uint8 para) {
}
}
-bool TownsPC98_OpnChannel::control_f0_setPatchSSG(uint8 para) {
- _instrID = para << 4;
+TownsPC98_OpnChannelSSG::TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,
+ uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
+ TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id) {
+}
+
+void TownsPC98_OpnChannelSSG::init() {
+ _algorithm = 0x80;
+
+ #define Control(x) &TownsPC98_OpnChannelSSG::control_##x
+ static const ControlEventFunc ctrlEventsSSG[] = {
+ Control(f0_setInstr),
+ Control(f1_setTotalLevel),
+ Control(f2_setKeyOffTime),
+ Control(f3_setFreqLSB),
+ Control(f4_setAlgorithm),
+ Control(f5_setTempo),
+ Control(f6_repeatSection),
+ Control(f7_setupPitchWheel),
+ Control(f8_togglePitchWheel),
+ 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_OpnChannelSSG::processEvents() {
+ if (_flags & CHS_EOT)
+ return;
+
+ _drv->_regProtectionFlag = (_flags & CHS_PROTECT) ? true : false;
+
+ if (!_hold && _ticksLeft == _keyOffTime)
+ nextShape();
+
+ if (!--_ticksLeft) {
+
+ uint8 cmd = 0;
+ bool loop = true;
+
+ while (loop) {
+ cmd = *_dataPtr++;
+ if (cmd < 0xf0)
+ loop = false;
+ else if (!processControlEvent(cmd))
+ return;
+ }
+
+ uint8 para = *_dataPtr++;
+
+ if (cmd == 0x80) {
+ nextShape();
+ _hold = false;
+ } else {
+ if (!_hold) {
+ _instr &= 0xf0;
+ _ssgStep = _drv->_ssgPatches[_instr];
+ _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+ _ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
+ _ssgStartLvl = _drv->_ssgPatches[_instr + 3];
+ _flags = (_flags & ~CHS_SSGOFF) | CHS_KEYOFF;
+ }
+
+ keyOn();
+
+ if (_hold == false || cmd != _frqBlockMSB)
+ _flags |= CHS_RECALCFREQ;
+
+ _hold = (para & 0x80) ? true : false;
+ _frqBlockMSB = cmd;
+ }
+
+ _ticksLeft = para & 0x7f;
+ }
+
+ if (!(_flags & CHS_SSGOFF)) {
+ if (--_ssgTicksLeft) {
+ if (!_drv->_fading)
+ setOutputLevel(_ssgStartLvl);
+ return;
+ }
+
+ _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+
+ if (_drv->_ssgPatches[_instr + 1] & 0x80) {
+ uint8 t = _ssgStartLvl - _ssgStep;
+
+ if (_ssgStep <= _ssgStartLvl && _ssgTargetLvl < t) {
+ if (!_drv->_fading)
+ setOutputLevel(t);
+ return;
+ }
+ } else {
+ int t = _ssgStartLvl + _ssgStep;
+ uint8 p = (uint8) (t & 0xff);
+
+ if (t < 256 && _ssgTargetLvl > p) {
+ if (!_drv->_fading)
+ setOutputLevel(p);
+ return;
+ }
+ }
+
+ setOutputLevel(_ssgTargetLvl);
+ if (_ssgStartLvl && !(_instr & 8)){
+ _instr += 4;
+ _ssgStep = _drv->_ssgPatches[_instr];
+ _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+ _ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
+ } else {
+ _flags |= CHS_SSGOFF;
+ setOutputLevel(0);
+ }
+ }
+}
+
+void TownsPC98_OpnChannelSSG::processFrequency() {
+ if (_algorithm & 0x40)
+ return;
+
+ if (_flags & CHS_RECALCFREQ) {
+ _block = _frqBlockMSB >> 4;
+ _frequency = ((const uint16*)_drv->_opnFreqTableSSG)[_frqBlockMSB & 0x0f] + _frqLSB;
+
+ uint16 f = _frequency >> _block;
+ writeReg(_regOffset << 1, f & 0xff);
+ writeReg((_regOffset << 1) + 1, f >> 8);
+
+ setupPitchWheel();
+ }
+
+ if (!(_flags & (CHS_EOT | CHS_PITCHWHEELOFF | CHS_SSGOFF))) {
+ if (!processPitchWheel())
+ return;
+
+ processPitchWheel();
+
+ uint16 f = _frequency >> _block;
+ writeReg(_regOffset << 1, f & 0xff);
+ writeReg((_regOffset << 1) + 1, f >> 8);
+ }
+}
+
+bool TownsPC98_OpnChannelSSG::processControlEvent(uint8 cmd) {
+ uint8 para = *_dataPtr++;
+ return (this->*controlEvents[cmd & 0x0f])(para);
+}
+
+void TownsPC98_OpnChannelSSG::nextShape() {
+ _instr = (_instr & 0xf0) + 0x0c;
+ _ssgStep = _drv->_ssgPatches[_instr];
+ _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
+ _ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
+}
+
+void TownsPC98_OpnChannelSSG::keyOn() {
+ uint8 c = 0x7b;
+ uint8 t = (_algorithm & 0xC0) << 1;
+ if (_algorithm & 0x80)
+ t |= 4;
+
+ c = (c << (_regOffset + 1)) | (c >> (7 - _regOffset));
+ t = (t << (_regOffset + 1)) | (t >> (7 - _regOffset));
+
+ if (!(_algorithm & 0x80))
+ writeReg(6, _algorithm & 0x7f);
+
+ uint8 e = (_drv->_ssg->readReg(7) & c) | t;
+ writeReg(7, e);
+}
+
+void TownsPC98_OpnChannelSSG::protect() {
+ _flags |= CHS_PROTECT;
+}
+
+void TownsPC98_OpnChannelSSG::restore() {
+ _flags &= ~CHS_PROTECT;
+ keyOn();
+ writeReg(8 + _regOffset, _ssgTl);
+ uint16 f = _frequency >> _block;
+ writeReg(_regOffset << 1, f & 0xff);
+ writeReg((_regOffset << 1) + 1, f >> 8);
+}
+
+void TownsPC98_OpnChannelSSG::loadData(uint8 *data) {
+ _drv->_regProtectionFlag = (_flags & CHS_PROTECT) ? true : false;
+ TownsPC98_OpnChannel::loadData(data);
+ setOutputLevel(0);
+ _algorithm = 0x80;
+}
+
+void TownsPC98_OpnChannelSSG::setOutputLevel(uint8 lvl) {
+ _ssgStartLvl = lvl;
+ uint16 newTl = (((uint16)_totalLevel + 1) * (uint16)lvl) >> 8;
+ if (newTl == _ssgTl)
+ return;
+ _ssgTl = newTl;
+ writeReg(8 + _regOffset, _ssgTl);
+}
+
+void TownsPC98_OpnChannelSSG::fadeStep() {
+ _totalLevel--;
+ if ((int8)_totalLevel < 0)
+ _totalLevel = 0;
+ setOutputLevel(_ssgStartLvl);
+}
+
+bool TownsPC98_OpnChannelSSG::control_f0_setInstr(uint8 para) {
+ _instr = para << 4;
para = (para >> 3) & 0x1e;
if (para)
return control_f4_setAlgorithm(para | 0x40);
return true;
}
-bool TownsPC98_OpnChannel::control_f1_setTotalLevel(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_f1_setTotalLevel(uint8 para) {
if (!_drv->_fading)
_totalLevel = para;
return true;
}
-bool TownsPC98_OpnChannel::control_f4_setAlgorithm(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_f4_setAlgorithm(uint8 para) {
_algorithm = para;
return true;
}
-bool TownsPC98_OpnChannel::control_f9_unkSSG(uint8 para) {
- _dataPtr += 5;
+bool TownsPC98_OpnChannelSSG::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++;
return true;
}
-bool TownsPC98_OpnChannel::control_fb_incOutLevelSSG(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_fb_incOutLevel(uint8 para) {
_dataPtr--;
if (_drv->_fading)
return true;
@@ -2175,7 +2550,7 @@ bool TownsPC98_OpnChannel::control_fb_incOutLevelSSG(uint8 para) {
return true;
}
-bool TownsPC98_OpnChannel::control_fc_decOutLevelSSG(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_fc_decOutLevel(uint8 para) {
_dataPtr--;
if (_drv->_fading)
return true;
@@ -2186,184 +2561,331 @@ bool TownsPC98_OpnChannel::control_fc_decOutLevelSSG(uint8 para) {
return true;
}
-bool TownsPC98_OpnChannel::control_ff_endOfTrackSSG(uint8 para) {
- uint16 val = READ_LE_UINT16(--_dataPtr);
- if (val) {
- // loop
- _dataPtr = _drv->_trackData + val;
- return true;
+bool TownsPC98_OpnChannelSSG::control_ff_endOfTrack(uint8 para) {
+ if (!_drv->_sfxOffs) {
+ uint16 val = READ_LE_UINT16(--_dataPtr);
+ if (val) {
+ // loop
+ _dataPtr = _drv->_trackPtr + val;
+ return true;
+ } else {
+ // stop parsing
+ if (!_drv->_fading)
+ setOutputLevel(0);
+ --_dataPtr;
+ _flags |= CHS_EOT;
+ _drv->_finishedSSGFlag |= _idFlag;
+ }
} else {
- // quit parsing for active channel
- --_dataPtr;
+ // end of sfx track - restore ssg music channel
_flags |= CHS_EOT;
- //_finishedChannelsFlag |= _idFlag;
- keyOff();
- return false;
+ _drv->_ssgChannels[_chanNum]->restore();
}
+
+ return false;
}
-TownsPC98_OpnChannelSSG::TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,
+void TownsPC98_OpnSfxChannel::loadData(uint8 *data) {
+ _flags = CHS_ALL_BUT_EOT;
+ _ticksLeft = 1;
+ _dataPtr = data;
+ _ssgTl = 0xff;
+ _algorithm = 0x80;
+}
+
+TownsPC98_OpnChannelPCM::TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs,
uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id) {
}
-void TownsPC98_OpnChannelSSG::init() {
+void TownsPC98_OpnChannelPCM::init() {
_algorithm = 0x80;
-
- _opr = new TownsPC98_OpnOperator*[4];
- for (int i = 0; i < 4; i++)
- _opr[i] = new TownsPC98_OpnOperator(_drv->_baserate, _drv->_oprRates, _drv->_oprRateshift,
- _drv->_oprAttackDecay, _drv->_oprFrq, _drv->_oprSinTbl, _drv->_oprLevelOut, _drv->_oprDetune);
- #define Control(x) &TownsPC98_OpnChannelSSG::control_##x
- static const ControlEventFunc ctrlEventsSSG[] = {
- Control(f0_setPatchSSG),
- Control(f1_setTotalLevel),
- Control(f2_setKeyOffTime),
- Control(f3_setFreqLSB),
- Control(f4_setOutputLevel),
- Control(f5_setTempo),
+ #define Control(x) &TownsPC98_OpnChannelPCM::control_##x
+ static const ControlEventFunc ctrlEventsPCM[] = {
+ Control(dummy),
+ Control(f1_pcmStart),
+ Control(dummy),
+ Control(dummy),
+ Control(dummy),
+ Control(dummy),
Control(f6_repeatSection),
- Control(f7_setupPitchWheel),
- Control(f8_togglePitchWheel),
- Control(f9_unkSSG),
+ Control(dummy),
+ Control(dummy),
+ Control(dummy),
Control(fa_writeReg),
- Control(fb_incOutLevelSSG),
- Control(fc_decOutLevelSSG),
- Control(fd_jump),
Control(dummy),
- Control(ff_endOfTrackSSG)
+ Control(dummy),
+ Control(dummy),
+ Control(dummy),
+ Control(ff_endOfTrack)
};
#undef Control
- controlEvents = ctrlEventsSSG;
+ controlEvents = ctrlEventsPCM;
}
-void TownsPC98_OpnChannelSSG::processEvents() {
+void TownsPC98_OpnChannelPCM::loadData(uint8 *data) {
+ _flags = (_flags & ~CHS_EOT) | CHS_ALL_BUT_EOT;
+ _ticksLeft = 1;
+ _dataPtr = data;
+ _totalLevel = 0x7F;
+}
+
+void TownsPC98_OpnChannelPCM::processEvents() {
if (_flags & CHS_EOT)
return;
- _drv->_ssgFlag = (_flags & CHS_SSG) ? -1 : 0;
-
- if (_protect == false && _ticksLeft == _keyOffTime)
- keyOff();
-
if (--_ticksLeft)
return;
- if (_protect == false)
- keyOff();
-
uint8 cmd = 0;
bool loop = true;
while (loop) {
cmd = *_dataPtr++;
- if (cmd < 0xf0)
+ if (cmd == 0x80) {
loop = false;
- else if (!processControlEvent(cmd))
+ } else if (cmd < 0xf0) {
+ writeReg(0x10, cmd);
+ } else if (!processControlEvent(cmd)) {
return;
+ }
}
+ _ticksLeft = *_dataPtr++;
+}
+
+bool TownsPC98_OpnChannelPCM::processControlEvent(uint8 cmd) {
uint8 para = *_dataPtr++;
+ return (this->*controlEvents[cmd & 0x0f])(para);
+}
- if (cmd == 0x80) {
- keyOff();
- _protect = false;
- } else {
- keyOn();
+void TownsPC98_OpnChannelPCM::fadeStep() {
- if (_protect == false || cmd != _frqBlockMSB)
- _flags |= CHS_RECALCFREQ;
-
- _protect = (para & 0x80) ? true : false;
- _frqBlockMSB = cmd;
+}
+
+bool TownsPC98_OpnChannelPCM::control_f1_pcmStart(uint8 para) {
+ _totalLevel = para;
+ writeReg(0x11, para);
+ return true;
+}
+
+bool TownsPC98_OpnChannelPCM::control_ff_endOfTrack(uint8 para) {
+ uint16 val = READ_LE_UINT16(--_dataPtr);
+ if (val) {
+ // loop
+ _dataPtr = _drv->_trackPtr + val;
+ return true;
+ } else {
+ // quit parsing for active channel
+ --_dataPtr;
+ _flags |= CHS_EOT;
+ _drv->_finishedPCMFlag |= _idFlag;
+ return false;
}
+}
- _ticksLeft = para & 0x7f;
+TownsPC98_SSG::TownsPC98_SSG(TownsPC98_OpnDriver *driver, Audio::Mixer *mixer, float rate) : _drv(driver), _mixer(mixer), _rate(rate),
+ _tlTable(0), _tleTable(0), _regIndex(_reg), _updateRequest(-1), _tickLength((int)(rate * 32768.0f * 27.0f)), _ready(0) {
+ memset(_reg, 0, 16);
+ memset(_channels, 0, sizeof(Channel) * 3);
+ reset();
+}
- if (!(_flags & CHS_SSG)) {
+TownsPC98_SSG::~TownsPC98_SSG() {
+ _mixer->stopHandle(_soundHandle);
- }
+ delete [] _tlTable;
+ delete [] _tleTable;
}
-void TownsPC98_OpnChannelSSG::processFrequency() {
- if (_flags & CHS_RECALCFREQ) {
- uint8 block = (_frqBlockMSB & 0x70) >> 1;
- uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f];
- frequency = (bfreq + _frqLSB) | (block << 8);
+void TownsPC98_SSG::init(const int *rsTable, const int *rseTable) {
+ if (_ready) {
+ reset();
+ return;
+ }
- writeReg(_regOffset + 0xa4, (frequency >> 8));
- writeReg(_regOffset + 0xa0, (frequency & 0xff));
+ delete [] _tlTable;
+ delete [] _tleTable;
+ _tlTable = new int32[16];
+ _tleTable = new int32[32];
+ float a, b, d;
+ d = 801.0f;
- _ptchWhlCurDelay = _ptchWhlInitDelayHi;
- if (_flags & CHS_KEYOFF) {
- _ptchWhlModCurVal = _ptchWhlModInitVal;
- _ptchWhlCurDelay += _ptchWhlInitDelayLo;
- }
+ for (int i = 0; i < 16; i++) {
+ b = 1.0f / rsTable[i];
+ a = 1.0f / d + b + 1.0f / 1000.0f;
+ float v = (b / a) * 32767.0f;
+ _tlTable[i] = (int32) v;
- _ptchWhlDurLeft = (_ptchWhlDuration >> 1);
- _flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
+ b = 1.0f / rseTable[i];
+ a = 1.0f / d + b + 1.0f / 1000.0f;
+ v = (b / a) * 32767.0f;
+ _tleTable[i] = (int32) v;
}
- if (!(_flags & CHS_PITCHWHEELOFF)) {
- if (--_ptchWhlCurDelay)
- return;
- _ptchWhlCurDelay = _ptchWhlInitDelayHi;
- frequency += _ptchWhlModCurVal;
+ for (int i = 16; i < 32; i++) {
+ b = 1.0f / rseTable[i];
+ a = 1.0f / d + b + 1.0f / 1000.0f;
+ float v = (b / a) * 32767.0f;
+ _tleTable[i] = (int32) v;
+ }
- writeReg(_regOffset + 0xa4, (frequency >> 8));
- writeReg(_regOffset + 0xa0, (frequency & 0xff));
+ _mixer->playInputStream(Audio::Mixer::kMusicSoundType,
+ &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true);
- if(!--_ptchWhlDurLeft) {
- _ptchWhlDurLeft = _ptchWhlDuration;
- _ptchWhlModCurVal = -_ptchWhlModCurVal;
- }
+ _ready = true;
+}
+
+void TownsPC98_SSG::reset() {
+ _rand = 1;
+ _outN = 1;
+ _updateRequest = -1;
+ _nTick = _evpUpdateCnt = 0;
+ _regIndex = _reg;
+ _evpTimer = 0x1f;
+ _pReslt = 0x1f;
+ _attack = 0;
+ _cont = false;
+ _evpUpdate = true;
+ _timer = 0;
+
+ for (int i = 0; i < 3; i++) {
+ _channels[i].tick = 0;
+ _channels[i].smp = _channels[i].out = 0;
}
+
+ for (int i = 0; i < 14; i++)
+ writeReg(i, 0, true);
+
+ writeReg(7, 0xbf, true);
}
-void TownsPC98_OpnChannelSSG::keyOff() {
- // all operators off
- uint8 value = _keyNum & 0x0f;
- uint8 regAdress = 0x28;
- writeReg(regAdress, value);
- _flags |= CHS_KEYOFF;
+uint8 TownsPC98_SSG::readReg(uint8 address) {
+ return _reg[address];
}
-void TownsPC98_OpnChannelSSG::keyOn() {
- // all operators on
- uint8 value = _keyNum | 0xf0;
- uint8 regAdress = 0x28;
- writeReg(regAdress, value);
+void TownsPC98_SSG::writeReg(uint8 address, uint8 value, bool force) {
+ _regIndex = &_reg[address];
+ int o = _regIndex - _reg;
+ if (!force && (o == 13 || *_regIndex != value)) {
+ if (_updateRequest == 31) {
+ warning("TownsPC98_SSG: event buffer overflow");
+ _updateRequest = -1;
+ }
+ _updateRequestBuf[++_updateRequest] = value;
+ _updateRequestBuf[++_updateRequest] = o;
+ return;
+ }
+
+ *_regIndex = value;
}
-void TownsPC98_OpnChannelSSG::loadData(uint8 *data) {
- _drv->_ssgFlag = (_flags & CHS_SSG) ? -1 : 0;
- opn_SSG_UNK(0);
- TownsPC98_OpnChannel::loadData(data);
- _algorithm = 0x80;
+void TownsPC98_SSG::updatesRegs() {
+ for (int i = 0; i < _updateRequest;) {
+ uint8 b = _updateRequestBuf[i++];
+ uint8 a = _updateRequestBuf[i++];
+ writeReg(a, b, true);
+ }
+ _updateRequest = -1;
}
-void TownsPC98_OpnChannelSSG::opn_SSG_UNK(uint8 a) {
- _ssg1 = a;
- uint16 h = (_totalLevel + 1) * a;
- if ((h >> 8) == _ssg2)
+void TownsPC98_SSG::nextTick(int16 *buffer, uint32 bufferSize) {
+ if (!_ready)
return;
- _ssg2 = (h >> 8);
- writeReg(8 + _regOffset, _ssg2);
+
+ for (uint32 i = 0; i < bufferSize; i++) {
+ _timer += _tickLength;
+ while (_timer > 0x30000) {
+ _timer -= 0x30000;
+
+ if (++_nTick >= (_reg[6] & 0x1f)) {
+ if ((_rand + 1) & 2)
+ _outN ^= 1;
+
+ _rand = (((_rand & 1) ^ ((_rand >> 3) & 1)) << 16) | (_rand >> 1);
+ _nTick = 0;
+ }
+
+ for (int ii = 0; ii < 3; ii++) {
+ if (++_channels[ii].tick >= (((_reg[ii * 2 + 1] & 0x0f) << 8) | _reg[ii * 2])) {
+ _channels[ii].tick = 0;
+ _channels[ii].smp ^= 1;
+ }
+ _channels[ii].out = (_channels[ii].smp | ((_reg[7] >> ii) & 1)) & (_outN | ((_reg[7] >> (ii + 3)) & 1));
+ }
+
+ if (_evpUpdate) {
+ if (++_evpUpdateCnt >= ((_reg[12] << 8) | _reg[11])) {
+ _evpUpdateCnt = 0;
+
+ if (--_evpTimer < 0) {
+ if (_cont) {
+ _evpTimer &= 0x1f;
+ } else {
+ _evpUpdate = false;
+ _evpTimer = 0;
+ }
+ }
+ }
+ }
+ _pReslt = _evpTimer ^ _attack;
+ updatesRegs();
+ }
+
+ int16 t[3];
+ for (int ii = 0; ii < 3; ii++) {
+ if ((_reg[ii + 8] >> 4) & 1)
+ t[ii] = _tleTable[_channels[ii].out ? _pReslt : 0];
+ else
+ t[ii] = _tlTable[_channels[ii].out ? (_reg[ii + 8] & 0x0f) : 0];
+ }
+
+ int l = (int) buffer[i << 1];
+ int r = (int) buffer[(i << 1) + 1];
+
+ for (int ii = 0; ii < 3; ii++) {
+ l += t[ii];
+ r += t[ii];
+ }
+
+ l /= 2;
+ r /= 2;
+
+ if (l > 32767)
+ l = 32767;
+ if (l < -32767)
+ l = -32767;
+ buffer[i << 1] = (int16)l;
+
+ if (r > 32767)
+ r = 32767;
+ if (r < -32767)
+ r = -32767;
+ buffer[(i << 1) + 1] = (int16)r;
+ }
}
+int TownsPC98_SSG::readBuffer(int16 *buffer, const int numSamples) {
+ memset(buffer, 0, sizeof(int16) * numSamples);
+ nextTick(buffer, numSamples >> 1);
+ return numSamples;
+}
+
TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) :
- _mixer(mixer), _trackData(0), _playing(false), _fading(false), _channels(0), _ssgChannels(0),
- _looping(0), _opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 84),
- _opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 220)) ,
- _oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0),
- _oprDetune(0), _cbCounter(4), _tickCounter(0), _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F),
- _finishedChannelsFlag(0), _samplesTillCallback(0), _samplesTillCallbackRemainder(0), _ready(false),
- _numSSG(type == OD_TOWNS ? 0 : 3), _hasADPCM(type == OD_TYPE86 ? true : false),
- _numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true) {
+ _mixer(mixer), _trackPtr(0), _musicPlaying(false), _sfxPlaying(false), _fading(false), _channels(0),
+ _ssgChannels(0), _sfxChannels(0), _pcmChannel(0), _looping(0), _opnCarrier(_drvTables + 76),
+ _opnFreqTable(_drvTables + 84), _opnFreqTableSSG(_drvTables + 252), _opnFxCmdLen(_drvTables + 36),
+ _opnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 228)), _oprRates(0), _oprRateshift(0), _oprAttackDecay(0),
+ _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), _oprDetune(0), _cbCounter(4), _tickCounter(0),
+ _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F), _updateSSGFlag(type == OD_TOWNS ? 0 : 7),
+ _updatePCMFlag(type == OD_TYPE86 ? 1 : 0), _finishedChannelsFlag(0), _finishedSSGFlag(0),
+ _finishedPCMFlag(0), _samplesTillCallback(0), _samplesTillCallbackRemainder(0),
+ _sfxData(0), _ready(false), _numSSG(type == OD_TOWNS ? 0 : 3), _hasPCM(type == OD_TYPE86 ? true : false),
+ _sfxOffs(0), _numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true),
+ _ssgPatches(0), _ssg(0), _baserate(55125.0f / (float)getRate()) {
setTempo(84);
- _baserate = (486202500.0 / (double)getRate()) / 10368.0;
}
TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {
@@ -2381,13 +2903,24 @@ TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {
delete [] _ssgChannels;
}
+ if (_sfxChannels) {
+ for (int i = 0; i < 2; i++)
+ delete _sfxChannels[i];
+ delete [] _sfxChannels;
+ }
+
+ if (_pcmChannel)
+ delete _pcmChannel;
+
+ delete _ssg;
delete [] _oprRates;
delete [] _oprRateshift;
delete [] _oprFrq;
delete [] _oprAttackDecay;
delete [] _oprSinTbl;
delete [] _oprLevelOut;
- delete [] _oprDetune;
+ delete [] _oprDetune;
+ delete [] _ssgPatches;
}
bool TownsPC98_OpnDriver::init() {
@@ -2420,7 +2953,21 @@ bool TownsPC98_OpnDriver::init() {
}
delete [] _ssgChannels;
}
+
+ if (_sfxChannels) {
+ for (int i = 0; i < 2; i++) {
+ if (_sfxChannels[i])
+ delete _sfxChannels[i];
+ }
+ delete [] _sfxChannels;
+ }
+
if (_numSSG) {
+ _ssg = new TownsPC98_SSG(this, _mixer, _baserate);
+ _ssg->init(&_ssgTables[0], &_ssgTables[16]);
+ _ssgPatches = new uint8[256];
+ memcpy(_ssgPatches, _drvTables + 244, 256);
+
_ssgChannels = new TownsPC98_OpnChannelSSG*[_numSSG];
for (int i = 0; i < _numSSG; i++) {
int ii = i * 6;
@@ -2428,6 +2975,20 @@ bool TownsPC98_OpnDriver::init() {
_drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]);
_ssgChannels[i]->init();
}
+
+ _sfxChannels = new TownsPC98_OpnSfxChannel*[2];
+ for (int i = 0; i < 2; i++) {
+ int ii = (i + 1) * 6;
+ _sfxChannels[i] = new TownsPC98_OpnSfxChannel(this, _drvTables[ii], _drvTables[ii + 1],
+ _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]);
+ _sfxChannels[i]->init();
+ }
+ }
+
+ if (_hasPCM) {
+ delete _pcmChannel;
+ _pcmChannel = new TownsPC98_OpnChannelPCM(this, 0, 0, 0, 0, 0, 1);
+ _pcmChannel->init();
}
_mixer->playInputStream(Audio::Mixer::kMusicSoundType,
@@ -2440,9 +3001,11 @@ bool TownsPC98_OpnDriver::init() {
int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples) {
memset(buffer, 0, sizeof(int16) * numSamples);
int32 samplesLeft = numSamples >> 1;
+
while (samplesLeft) {
if (!_samplesTillCallback) {
callback();
+
_samplesTillCallback = _samplesPerCallback;
_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
if (_samplesTillCallbackRemainder >= _tempo) {
@@ -2458,8 +3021,19 @@ int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples)
nextTick(buffer, render);
for (int i = 0; i < render; ++i) {
- buffer[i << 1] <<= 2;
- buffer[(i << 1) + 1] <<= 2;
+ int l = (int) (buffer[i << 1] * 7) / 2;
+ if (l > 32767)
+ l = 32767;
+ if (l < -32767)
+ l = -32767;
+ buffer[i << 1] = (int16) l;
+
+ int r = (int) (buffer[(i << 1) + 1] * 7) / 2;
+ if (r > 32767)
+ r = 32767;
+ if (r < -32767)
+ r = -32767;
+ buffer[(i << 1) + 1] = (int16) r;
}
buffer += (render << 1);
@@ -2468,7 +3042,7 @@ int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples)
return numSamples;
}
-void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) {
+void TownsPC98_OpnDriver::loadMusicData(uint8 *data, bool loadPaused) {
if (!_ready) {
warning("TownsPC98_OpnDriver: Driver must be initialized before loading data");
return;
@@ -2479,12 +3053,11 @@ void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) {
return;
}
- lock();
- _trackData = data;
-
reset();
+
+ lock();
- uint8 *src_a = data;
+ uint8 *src_a = _trackPtr = _musicBuffer = data;
for (uint8 i = 0; i < 3; i++) {
_channels[i]->loadData(data + READ_LE_UINT16(src_a));
@@ -2501,117 +3074,167 @@ void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) {
src_a += 2;
}
- if (_hasADPCM) {
- //_adpcmChannel->loadData(data + READ_LE_UINT16(src_a));
+ if (_hasPCM) {
+ _pcmChannel->loadData(data + READ_LE_UINT16(src_a));
src_a += 2;
}
- _ssgFlag = 0;
+ _regProtectionFlag = false;
_patches = src_a + 4;
_cbCounter = 4;
- _finishedChannelsFlag = 0;
+ _finishedChannelsFlag = _finishedSSGFlag = 0;
+
+ _musicPlaying = (loadPaused ? false : true);
+
+ unlock();
+}
+
+void TownsPC98_OpnDriver::loadSoundEffectData(uint8 *data, uint8 trackNum) {
+ if (!_ready) {
+ warning("TownsPC98_OpnDriver: Driver must be initialized before loading data");
+ return;
+ }
+
+ if (!_sfxChannels) {
+ warning("TownsPC98_OpnDriver: sound effects not supported by this configuration");
+ return;
+ }
- // AH 0x17
+ if (!data) {
+ warning("TownsPC98_OpnDriver: Invalid sound effect file data");
+ return;
+ }
+
+ lock();
+ _sfxData = _sfxBuffer = data;
+ _sfxOffsets[0] = READ_LE_UINT16(&_sfxData[(trackNum << 2)]);
+ _sfxOffsets[1] = READ_LE_UINT16(&_sfxData[(trackNum << 2) + 2]);
+ _sfxPlaying = true;
unlock();
- _playing = (loadPaused ? false : true);
}
void TownsPC98_OpnDriver::reset() {
- for (int i = 0; i < (_numChan); i++)
+ lock();
+
+ for (int i = 0; i < _numChan; i++)
_channels[i]->reset();
- for (int i = 0; i < (_numSSG); i++)
+ for (int i = 0; i < _numSSG; i++)
_ssgChannels[i]->reset();
+
+ if (_ssg) {
+ for (int i = 0; i < 2; i++)
+ _sfxChannels[i]->reset();
+
+ memcpy(_ssgPatches, _drvTables + 276, 256);
+ _ssg->reset();
+ }
- _playing = _fading = false;
+ _musicPlaying = _sfxPlaying = _fading = false;
_looping = 0;
_tickCounter = 0;
+ _sfxData = 0;
+
+ unlock();
}
void TownsPC98_OpnDriver::fadeOut() {
- if (!_playing)
+ if (!_musicPlaying)
return;
- _fading = true;
-
for (int i = 0; i < 20; i++) {
lock();
+
+ _fading = true;
+
uint32 dTime = _tickCounter + 2;
for (int j = 0; j < _numChan; j++) {
if (_updateChannelsFlag & _channels[j]->_idFlag)
_channels[j]->fadeStep();
}
- for (int j = 0; j < _numSSG; j++)
- _ssgChannels[j]->fadeStep();
+ for (int j = 0; j < _numSSG; j++) {
+ if (_updateSSGFlag & _channels[j]->_idFlag)
+ _ssgChannels[j]->fadeStep();
+ }
unlock();
- while (_playing) {
+ while (_musicPlaying) {
if (_tickCounter >= dTime)
break;
}
}
- _fading = false;
-
reset();
}
void TownsPC98_OpnDriver::callback() {
- if (!_playing || --_cbCounter)
- return;
+ lock();
- _cbCounter = 4;
_tickCounter++;
- lock();
+ if (_sfxChannels && _sfxPlaying) {
+ if (_sfxData)
+ startSoundEffect();
- for (int i = 0; i < _numChan; i++) {
- if (_updateChannelsFlag & _channels[i]->_idFlag) {
- _channels[i]->processEvents();
- _channels[i]->processFrequency();
+ _sfxOffs = 3;
+ _trackPtr = _sfxBuffer;
+
+ for (int i = 0; i < 2; i++) {
+ _sfxChannels[i]->processEvents();
+ _sfxChannels[i]->processFrequency();
}
+
+ _trackPtr = _musicBuffer;
}
- if (_numSSG) {
- for (int i = 0; i < _numSSG; i++) {
- _ssgChannels[i]->processEvents();
- _ssgChannels[i]->processFrequency();
+ _sfxOffs = 0;
+
+ if (!--_cbCounter && _musicPlaying) {
+ _cbCounter = 4;
+
+ for (int i = 0; i < _numChan; i++) {
+ if (_updateChannelsFlag & _channels[i]->_idFlag) {
+ _channels[i]->processEvents();
+ _channels[i]->processFrequency();
+ }
}
+
+ if (_numSSG) {
+ for (int i = 0; i < _numSSG; i++) {
+ if (_updateSSGFlag & _ssgChannels[i]->_idFlag) {
+ _ssgChannels[i]->processEvents();
+ _ssgChannels[i]->processFrequency();
+ }
+ }
+ }
+
+ if (_hasPCM)
+ if (_updatePCMFlag & _pcmChannel->_idFlag)
+ _pcmChannel->processEvents();
}
-
- _ssgFlag = 0;
- unlock();
+ _regProtectionFlag = false;
- if (_finishedChannelsFlag == _updateChannelsFlag)
- reset();
+ if (_finishedChannelsFlag == _updateChannelsFlag && _finishedSSGFlag == _updateSSGFlag && _finishedPCMFlag == _updatePCMFlag)
+ _musicPlaying = false;
+
+ unlock();
}
void TownsPC98_OpnDriver::nextTick(int16 *buffer, uint32 bufferSize) {
- if (!_playing)
+ if (!_ready)
return;
- for (int i = 0; i < _numChan ; i++) {
- if (_channels[i]->_updateEnvelopes) {
- _channels[i]->_updateEnvelopes = false;
+ for (int i = 0; i < _numChan; i++) {
+ if (_channels[i]->_updateEnvelopeParameters) {
+ _channels[i]->_updateEnvelopeParameters = false;
_channels[i]->updateEnv();
}
for (uint32 ii = 0; ii < bufferSize ; ii++)
_channels[i]->generateOutput(buffer[ii * 2],
- buffer[ii * 2 + 1], &_channels[i]->_feedbuf[2], _channels[i]->_feedbuf);
- }
-
- for (int i = 0; i < _numSSG ; i++) {
- if (_ssgChannels[i]->_updateEnvelopes) {
- _ssgChannels[i]->_updateEnvelopes = false;
- _ssgChannels[i]->updateEnv();
- }
-
- for (uint32 ii = 0; ii < bufferSize ; ii++)
- _ssgChannels[i]->generateOutput(buffer[ii * 2],
- buffer[ii * 2 + 1], &_ssgChannels[i]->_feedbuf[2], _ssgChannels[i]->_feedbuf);
+ buffer[ii * 2 + 1], &_channels[i]->_feedbuf[2], _channels[i]->_feedbuf);
}
}
@@ -2641,7 +3264,7 @@ void TownsPC98_OpnDriver::generateTables() {
delete [] _oprFrq;
_oprFrq = new uint32[0x1000];
for (uint32 i = 0; i < 0x1000; i++)
- _oprFrq[i] = (uint32)(_baserate * (double)(i << 11));
+ _oprFrq[i] = (uint32)(_baserate * (float)(i << 11));
delete [] _oprAttackDecay;
_oprAttackDecay = new uint8[152];
@@ -2680,17 +3303,29 @@ void TownsPC98_OpnDriver::generateTables() {
delete [] _oprDetune;
_oprDetune = new int32[256];
for (int i = 0; i < 128; i++) {
- _oprDetune[i] = (int32) ((double)dtt[i] * _baserate * 64.0);
+ _oprDetune[i] = (int32) ((float)dtt[i] * _baserate * 64.0);
_oprDetune[i + 128] = -_oprDetune[i];
}
delete [] dtt;
}
+void TownsPC98_OpnDriver::startSoundEffect() {
+ for (int i = 0; i < 2; i++) {
+ if (_sfxOffsets[i]) {
+ _ssgChannels[i + 1]->protect();
+ _sfxChannels[i]->reset();
+ _sfxChannels[i]->loadData(_sfxData + _sfxOffsets[i]);
+ }
+ }
+
+ _sfxData = 0;
+}
+
void TownsPC98_OpnDriver::setTempo(uint8 tempo) {
- _tempo = tempo;
- _samplesPerCallback = getRate() / _tempo;
- _samplesPerCallbackRemainder = getRate() % _tempo;
+ _tempo = tempo - 0x10;
+ _samplesPerCallback = _tempo ? (getRate() / _tempo) : 0;
+ _samplesPerCallbackRemainder = _tempo ? (getRate() % _tempo) : 0;
}
const uint8 TownsPC98_OpnDriver::_drvTables[] = {
@@ -2742,7 +3377,47 @@ const uint8 TownsPC98_OpnDriver::_drvTables[] = {
// pc98 level presets
0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25,
0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10,
- 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90
+ 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90,
+
+ // 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
};
const uint32 TownsPC98_OpnDriver::_adtStat[] = {
@@ -2757,6 +3432,15 @@ const uint32 TownsPC98_OpnDriver::_adtStat[] = {
0x08080808, 0x08080808, 0x10101010, 0x10101010
};
+const int TownsPC98_OpnDriver::_ssgTables[] = {
+ 0x01202A, 0x0092D2, 0x006B42, 0x0053CB, 0x003DF8, 0x003053, 0x0022DA, 0x001A8C,
+ 0x00129B, 0x000DC1, 0x000963, 0x0006C9, 0x000463, 0x0002FA, 0x0001B6, 0x0000FB,
+ 0x0193B6, 0x01202A, 0x00CDB1, 0x0092D2, 0x007D7D, 0x006B42, 0x005ECD, 0x0053CB,
+ 0x00480F, 0x003DF8, 0x0036B9, 0x003053, 0x00290A, 0x0022DA, 0x001E6B, 0x001A8C,
+ 0x001639, 0x00129B, 0x000FFF, 0x000DC1, 0x000B5D, 0x000963, 0x0007FB, 0x0006C9,
+ 0x000575, 0x000463, 0x00039D, 0x0002FA, 0x000242, 0x0001B6, 0x00014C, 0x0000FB
+};
+
SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
: Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _sfxFileData(0),
_sfxFileIndex((uint)-1), _sfxWDTable(0), _sfxBTTable(0), _parser(0) {
@@ -2916,7 +3600,8 @@ void SoundTowns::playSoundEffect(uint8 track) {
}
playbackBufferSize -= 0x20;
- uint32 outputRate = uint32(11025 * semitoneAndSampleRate_to_sampleStep(note, sfxRootNoteOffs, sfxRate, 11025, 0x2000));
+
+ uint32 outputRate = uint32(11025 * calculatePhaseStep(note, sfxRootNoteOffs, sfxRate, 11025, 0x2000));
_currentSFX = Audio::makeLinearInputStream(sfxPlaybackBuffer, playbackBufferSize,
outputRate, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0);
@@ -2995,7 +3680,7 @@ void SoundTowns::onTimer(void *data) {
music->_parser->onTimer();
}
-float SoundTowns::semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey,
+float SoundTowns::calculatePhaseStep(int8 semiTone, int8 semiToneRootkey,
uint32 sampleRate, uint32 outputRate, int32 pitchWheel) {
if (semiTone < 0)
semiTone = 0;
@@ -3054,14 +3739,14 @@ void SoundPC98::playTrack(uint8 track) {
if (track == _lastTrack && _musicEnabled)
return;
- haltTrack();
+ beginFadeOut();
char musicfile[13];
sprintf(musicfile, fileListEntry(0), track);
delete[] _musicTrackData;
_musicTrackData = _vm->resource()->fileData(musicfile, 0);
if (_musicEnabled)
- _driver->loadData(_musicTrackData);
+ _driver->loadMusicData(_musicTrackData);
_lastTrack = track;
}
@@ -3078,25 +3763,32 @@ void SoundPC98::beginFadeOut() {
haltTrack();
}
-void SoundPC98::playSoundEffect(uint8) {
- /// TODO ///
+void SoundPC98::playSoundEffect(uint8 track) {
+ if (!_sfxTrackData)
+ return;
+
+ // This has been disabled for now since I don't know
+ // how to make up the correct track number. It probably
+ // needs a map.
+ //_driver->loadSoundEffectData(_sfxTrackData, track);
}
// KYRA 2
SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
- Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false) {
+ Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false) {
}
SoundTownsPC98_v2::~SoundTownsPC98_v2() {
delete[] _musicTrackData;
+ delete[] _sfxTrackData;
delete _driver;
}
bool SoundTownsPC98_v2::init() {
- _driver = new TownsPC98_OpnDriver(_mixer, /*_vm->gameFlags().platform == Common::kPlatformPC98 ?
- TownsPC98_OpnDriver::OD_TYPE86 :*/ TownsPC98_OpnDriver::OD_TOWNS);
+ _driver = new TownsPC98_OpnDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
+ TownsPC98_OpnDriver::OD_TYPE86 : TownsPC98_OpnDriver::OD_TOWNS);
_useFmSfx = _vm->gameFlags().platform == Common::kPlatformPC98 ? true : false;
_vm->checkCD();
// FIXME: While checking for 'track1.XXX(X)' looks like
@@ -3110,9 +3802,15 @@ bool SoundTownsPC98_v2::init() {
(Common::File::exists("track1.mp3") || Common::File::exists("track1.ogg") ||
Common::File::exists("track1.flac") || Common::File::exists("track1.fla")))
_musicEnabled = 2;
+
return _driver->init();
}
+void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
+ delete [] _sfxTrackData;
+ _sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
+}
+
void SoundTownsPC98_v2::process() {
AudioCD.updateCD();
}
@@ -3140,7 +3838,7 @@ void SoundTownsPC98_v2::playTrack(uint8 track) {
delete[] _musicTrackData;
_musicTrackData = _vm->resource()->fileData(musicfile, 0);
- _driver->loadData(_musicTrackData, true);
+ _driver->loadMusicData(_musicTrackData, true);
if (_musicEnabled == 2 && trackNum != -1) {
AudioCD.play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
@@ -3221,7 +3919,7 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, bool) {
sfx[i] = cmd;
}
- uint32 outputRate = uint32(11025 * SoundTowns::semitoneAndSampleRate_to_sampleStep(0x3c, 0x3c, sfxRate, 11025, 0x2000));
+ uint32 outputRate = uint32(11025 * SoundTowns::calculatePhaseStep(0x3c, 0x3c, sfxRate, 11025, 0x2000));
_currentSFX = Audio::makeLinearInputStream(sfx, outsize, outputRate,
Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0);
@@ -3233,18 +3931,12 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, bool) {
}
void SoundTownsPC98_v2::playSoundEffect(uint8 track) {
- if (!_useFmSfx)
+ if (!_useFmSfx || !_sfxTrackData)
return;
-
- uint8 *sd = _vm->resource()->fileData("sound.dat", 0);
-
-
- //TODO
-
- delete [] sd;
+
+ _driver->loadSoundEffectData(_sfxTrackData, track);
}
} // end of namespace Kyra
#undef EUPHONY_FADEOUT_TICKS
-