aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/sound_towns.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/kyra/sound_towns.cpp')
-rw-r--r--engines/kyra/sound_towns.cpp1890
1 files changed, 1410 insertions, 480 deletions
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index ec1962a58f..abda839ab4 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -23,7 +23,6 @@
*
*/
-
#include "common/system.h"
#include "kyra/resource.h"
#include "kyra/sound.h"
@@ -64,13 +63,13 @@ public:
MidiDriver *device() { return 0; }
byte getNumber() { return 0; }
void release() { }
- void send(uint32 b) { }
+ void send(uint32) { }
void noteOff(byte note);
void noteOn(byte note, byte onVelo);
- void programChange(byte program) {}
+ void programChange(byte) {}
void pitchBend(int16 value);
void controlChange(byte control, byte value);
- void pitchBendFactor(byte value) { }
+ void pitchBendFactor(byte) { }
void sysEx_customInstrument(uint32 unused, const byte *instr);
protected:
@@ -427,7 +426,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;
@@ -819,7 +818,8 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {
}
}
- while (true) {
+ bool loop = true;
+ while (loop) {
byte cmd = *pos;
byte evt = (cmd & 0xF0);
@@ -853,7 +853,7 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {
info.basic.param2 = onVelo;
pos += 12;
- break;
+ loop = false;
} else {
pos += 6;
}
@@ -870,7 +870,7 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {
info.basic.param1 = pos[4];
info.basic.param2 = pos[5];
pos += 6;
- break;
+ loop = false;
} else {
pos += 6;
}
@@ -889,7 +889,7 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {
_tempo[2] = tempo & 0xff;
info.ext.data = (byte*) _tempo;
pos += 6;
- break;
+ loop = false;
} else if (cmd == 0xFD || cmd == 0xFE) {
// End of track.
if (_autoLoop) {
@@ -906,12 +906,12 @@ void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {
info.event = 0xFF;
info.ext.type = 0x2F;
info.ext.data = pos;
- break;
+ loop = false;
} else {
error("Unknown Euphony music event 0x%02X", (int)cmd);
memset(&info, 0, sizeof(info));
pos = 0;
- break;
+ loop = false;
}
}
_position._play_pos = pos;
@@ -1085,7 +1085,7 @@ void Towns_EuphonyTrackQueue::initDriver() {
class TownsPC98_OpnOperator {
public:
- TownsPC98_OpnOperator(double rate, const uint8 *rateTable,
+ TownsPC98_OpnOperator(const uint32 timerbase, 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(int32 phasebuf, int32 *feedbuf, int32 &out);
void feedbackLevel(int32 level) {_feedbackLevel = level ? level + 6 : 0; }
void detune(int value) { _detn = &_detnTbl[value << 5]; }
@@ -1111,6 +1111,7 @@ public:
protected:
EnvelopeState _state;
+ bool _playing;
uint32 _feedbackLevel;
uint32 _multiple;
uint32 _totalLevel;
@@ -1137,8 +1138,8 @@ protected:
const int32 *_tLvlTbl;
const int32 *_detnTbl;
- const double _tickLength;
- double _tick;
+ const uint32 _tickLength;
+ uint32 _timer;
int32 _currentLevel;
struct EvpState {
@@ -1147,23 +1148,31 @@ protected:
} fs_a, fs_d, fs_s, fs_r;
};
-TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, const uint8 *rateTable,
+TownsPC98_OpnOperator::TownsPC98_OpnOperator(const uint32 timerbase, 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(timerbase * 2),
_specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0),
_phase(0), _state(s_ready) {
-
+
reset();
}
void TownsPC98_OpnOperator::keyOn() {
+ if (_playing)
+ return;
+
+ _playing = true;
_state = s_attacking;
_phase = 0;
}
void TownsPC98_OpnOperator::keyOff() {
+ if (!_playing)
+ return;
+
+ _playing = false;
if (_state != s_ready)
_state = s_releasing;
}
@@ -1172,6 +1181,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,44 +1214,44 @@ void TownsPC98_OpnOperator::recalculateRates() {
fs_r.shift = _rshiftTbl[r + k];
}
-void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out) {
+void TownsPC98_OpnOperator::generateOutput(int32 phasebuf, int32 *feed, int32 &out) {
if (_state == s_ready)
return;
- _tick += _tickLength;
- while (_tick > 0x30000) {
- _tick -= 0x30000;
+ _timer += _tickLength;
+ while (_timer > 0x5B8D80) {
+ _timer -= 0x5B8D80;
++_tickCount;
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,31 +1259,31 @@ 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;
}
}
}
uint32 lvlout = _totalLevel + (uint32) _currentLevel;
- int outp = 0;
- int *i = &outp, *o = &outp;
+
+ int32 outp = 0;
+ int32 *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;
} else {
phaseShift = phasebuf << 15;
- }
+ }
if (lvlout < 832) {
uint32 index = (lvlout << 3) + _sinTbl[(((int32)((_phase & 0xffff0000)
@@ -1285,15 +1295,11 @@ void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out
_phase += _phaseIncrement;
out += *o;
- if (out > 32767)
- out = 32767;
- if (out < -32767)
- out = -32767;
}
void TownsPC98_OpnOperator::reset(){
keyOff();
- _tick = 0;
+ _timer = 0;
_keyScale2 = 0;
_currentLevel = 1023;
@@ -1306,7 +1312,7 @@ void TownsPC98_OpnOperator::reset(){
decayRate(0);
releaseRate(0);
sustainRate(0);
- feedbackLevel(0);
+ feedbackLevel(0);
totalLevel(127);
}
@@ -1332,40 +1338,42 @@ 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);
- void writeReg(uint8 regAdress, uint8 value);
+ virtual bool processControlEvent(uint8 cmd);
+ void writeReg(uint8 regAddress, uint8 value);
virtual void keyOn();
- virtual void keyOff();
-
+ void keyOff();
+
void setOutputLevel();
- void fadeStep();
- void reset();
+ virtual void fadeStep();
+ virtual void reset();
void updateEnv();
- void generateOutput(int16 &leftSample, int16 &rightSample, int *del, int *feed);
+ void generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed);
bool _enableLeft;
bool _enableRight;
- bool _updateEnvelopes;
+ bool _updateEnvelopeParameters;
const uint8 _idFlag;
- int _feedbuf[3];
+ int32 _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 +1385,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 +1424,7 @@ protected:
TownsPC98_OpnOperator **_opr;
uint16 _frqTemp;
+ typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para);
const ControlEventFunc *controlEvents;
};
@@ -1427,24 +1432,161 @@ 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 reset();
+
+private:
+ bool control_f1_pcmStart(uint8 para);
+ bool control_ff_endOfTrack(uint8 para);
+
+ typedef bool (TownsPC98_OpnChannelPCM::*ControlEventFunc)(uint8 para);
+ const ControlEventFunc *controlEvents;
+};
+
+class TownsPC98_OpnSquareSineSource {
+public:
+ TownsPC98_OpnSquareSineSource(const uint32 timerbase);
+ ~TownsPC98_OpnSquareSineSource();
+
+ void init(const int *rsTable, const int *rseTable);
+ void reset();
+ uint8 readReg(uint8 address);
+ void writeReg(uint8 address, uint8 value, bool force = false);
+
+ void nextTick(int32 *buffer, uint32 bufferSize);
private:
- void opn_SSG_UNK(uint8 a);
+ void updatesRegs();
+
+ 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 uint32 _tickLength;
+ uint32 _timer;
+
+ struct Channel {
+ int tick;
+ uint8 smp;
+ uint8 out;
+ } _channels[3];
+
+ bool _ready;
};
+class TownsPC98_OpnPercussionSource {
+public:
+ TownsPC98_OpnPercussionSource(const uint32 timerbase);
+ ~TownsPC98_OpnPercussionSource() {}
+
+ void init(const uint8 *pcmData = 0);
+ void reset();
+ void writeReg(uint8 address, uint8 value);
+
+ void nextTick(int32 *buffer, uint32 bufferSize);
+
+private:
+ struct PcmInstrument {
+ const uint8 *data;
+
+ const uint8 *start;
+ const uint8 *end;
+ const uint8 *pos;
+ uint32 size;
+ bool active;
+ uint8 level;
+
+ int8 decState;
+ uint8 decStep;
+
+ int16 samples[2];
+ int out;
+ };
+
+ void recalcOuput(PcmInstrument *ins);
+ void advanceInput(PcmInstrument *ins);
+
+ PcmInstrument _pcmInstr[6];
+ uint8 _regs[48];
+
+ uint8 _totalLevel;
+
+ const uint32 _tickLength;
+ uint32 _timer;
+ 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,17 +1598,21 @@ 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 fadeStep();
+
+ void pause() { _musicPlaying = false; }
+ void cont() { _musicPlaying = true; }
- void callback();
- void nextTick(int16 *buffer, uint32 bufferSize);
+ void musicCallback();
+ void sfxCallback();
+ void nextTick(int32 *buffer, uint32 bufferSize);
bool looping() { return _looping == _updateChannelsFlag ? true : false; }
+ bool musicPlaying() { return _musicPlaying; }
// AudioStream interface
int inline readBuffer(int16 *buffer, const int numSamples);
@@ -1479,9 +1625,16 @@ protected:
TownsPC98_OpnChannel **_channels;
TownsPC98_OpnChannelSSG **_ssgChannels;
- //TownsPC98_OpnChannel *_adpcmChannel;
+ TownsPC98_OpnSfxChannel **_sfxChannels;
+ TownsPC98_OpnChannelPCM *_pcmChannel;
- void setTempo(uint8 tempo);
+ TownsPC98_OpnSquareSineSource *_ssg;
+ TownsPC98_OpnPercussionSource *_pcm;
+
+ void startSoundEffect();
+
+ void setMusicTempo(uint8 tempo);
+ void setSfxTempo(uint16 tempo);
void lock() { _mutex.lock(); }
void unlock() { _mutex.unlock(); }
@@ -1492,6 +1645,7 @@ protected:
const uint8 *_opnCarrier;
const uint8 *_opnFreqTable;
+ const uint8 *_opnFreqTableSSG;
const uint8 *_opnFxCmdLen;
const uint8 *_opnLvlPresets;
@@ -1503,34 +1657,54 @@ 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 _updateSfxFlag;
uint8 _finishedChannelsFlag;
- uint16 _tempo;
- bool _playing;
- bool _fading;
+ uint8 _finishedSSGFlag;
+ uint8 _finishedPCMFlag;
+ uint8 _finishedSfxFlag;
+
+ bool _musicPlaying;
+ bool _sfxPlaying;
+ uint8 _fading;
uint8 _looping;
- uint32 _tickCounter;
+ uint32 _musicTickCounter;
- bool _updateEnvelopes;
- int _ssgFlag;
+ bool _updateEnvelopeParameters;
- int32 _samplesTillCallback;
- int32 _samplesTillCallbackRemainder;
- int32 _samplesPerCallback;
- int32 _samplesPerCallbackRemainder;
+ bool _regProtectionFlag;
+ int _sfxOffs;
+ uint8 *_sfxData;
+ uint16 _sfxOffsets[2];
+
+ int32 _samplesTillMusicCallback;
+ uint32 _samplesTillMusicCallbackRemainder;
+ int32 _samplesPerMusicCallback;
+ uint32 _samplesPerMusicCallbackRemainder;
+ int32 _samplesTillSfxCallback;
+ uint32 _samplesTillSfxCallbackRemainder;
+ int32 _samplesPerSfxCallback;
+ uint32 _samplesPerSfxCallbackRemainder;
const int _numChan;
const int _numSSG;
- const bool _hasADPCM;
- const bool _hasStereo;
+ const bool _hasPCM;
- double _baserate;
static const uint8 _drvTables[];
static const uint32 _adtStat[];
+ static const int _ssgTables[];
+
+ const float _baserate;
+ uint32 _timerbase;
+
bool _ready;
};
@@ -1538,14 +1712,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;
+ _dataPtr = 0;
_ptchWhlModInitVal = _ptchWhlModCurVal = 0;
- frequency = _frqTemp = 0;
+ _frequency = _frqTemp = 0;
memset(&_feedbuf, 0, sizeof(int) * 3);
_opr = 0;
}
@@ -1559,10 +1734,9 @@ TownsPC98_OpnChannel::~TownsPC98_OpnChannel() {
}
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,
+ _opr[i] = new TownsPC98_OpnOperator(_drv->_timerbase, _drv->_oprRates, _drv->_oprRateshift,
_drv->_oprAttackDecay, _drv->_oprFrq, _drv->_oprSinTbl, _drv->_oprLevelOut, _drv->_oprDetune);
#define Control(x) &TownsPC98_OpnChannel::control_##x
@@ -1592,16 +1766,16 @@ void TownsPC98_OpnChannel::init() {
void TownsPC98_OpnChannel::keyOff() {
// all operators off
uint8 value = _keyNum & 0x0f;
- uint8 regAdress = 0x28;
- writeReg(regAdress, value);
+ uint8 regAddress = 0x28;
+ writeReg(regAddress, value);
_flags |= CHS_KEYOFF;
}
void TownsPC98_OpnChannel::keyOn() {
// all operators on
uint8 value = _keyNum | 0xf0;
- uint8 regAdress = 0x28;
- writeReg(regAdress, value);
+ uint8 regAddress = 0x28;
+ writeReg(regAddress, value);
}
void TownsPC98_OpnChannel::loadData(uint8 *data) {
@@ -1645,13 +1819,13 @@ void TownsPC98_OpnChannel::processEvents() {
if (_flags & CHS_EOT)
return;
- if (_protect == false && _ticksLeft == _keyOffTime)
+ if (!_hold && _ticksLeft == _keyOffTime)
keyOff();
if (--_ticksLeft)
return;
- if (_protect == false)
+ if (!_hold)
keyOff();
uint8 cmd = 0;
@@ -1669,14 +1843,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 +1861,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));
+ _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;
- }
-
- _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 +1928,24 @@ 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();
+ }
- _updateEnvelopes = false;
+ _block = 0;
+ _frequency = 0;
+ _hold = false;
+ _frqTemp = 0;
+ _ssgTl = 0;
+ _ssgStartLvl = 0;
+ _ssgTargetLvl = 0;
+ _ssgStep = 0;
+ _ssgTicksLeft = 0;
+ _totalLevel = 0;
+ _flags |= CHS_EOT;
+
+ _updateEnvelopeParameters = false;
_enableLeft = _enableRight = true;
memset(&_feedbuf, 0, sizeof(int) * 3);
}
@@ -1756,10 +1955,10 @@ void TownsPC98_OpnChannel::updateEnv() {
_opr[i]->updatePhaseIncrement();
}
-void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample, int *del, int *feed) {
- int phbuf1, phbuf2, output;
+void TownsPC98_OpnChannel::generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed) {
+ int32 phbuf1, phbuf2, output;
phbuf1 = phbuf2 = output = 0;
-
+
switch (_algorithm) {
case 0:
_opr[0]->generateOutput(0, feed, phbuf1);
@@ -1771,7 +1970,7 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,
case 1:
_opr[0]->generateOutput(0, feed, phbuf1);
_opr[2]->generateOutput(*del, 0, phbuf2);
- _opr[1]->generateOutput(0, 0, phbuf1);
+ _opr[1]->generateOutput(0, 0, phbuf1);
_opr[3]->generateOutput(phbuf2, 0, output);
*del = phbuf1;
break;
@@ -1792,7 +1991,7 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,
case 4:
_opr[0]->generateOutput(0, feed, phbuf1);
_opr[2]->generateOutput(0, 0, phbuf2);
- _opr[1]->generateOutput(phbuf1, 0, output);
+ _opr[1]->generateOutput(phbuf1, 0, output);
_opr[3]->generateOutput(phbuf2, 0, output);
*del = 0;
break;
@@ -1819,39 +2018,34 @@ void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample,
break;
};
- if (_enableLeft) {
- int l = output + leftSample;
- if (l > 32767)
- l = 32767;
- if (l < -32767)
- l = -32767;
- leftSample = (int16) l;
- }
+ int32 finOut = ((output * 7) / 2);
- if (_enableRight) {
- int r = output + rightSample;
- if (r > 32767)
- r = 32767;
- if (r < -32767)
- r = -32767;
- rightSample = (int16) r;
- }
+ if (_enableLeft)
+ leftSample += finOut;
+
+ if (_enableRight)
+ rightSample += finOut;
}
-void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
- uint8 h = regAdress & 0xf0;
- uint8 l = (regAdress & 0x0f);
+void TownsPC98_OpnChannel::writeReg(uint8 regAddress, uint8 value) {
+ if (_drv->_regProtectionFlag)
+ return;
+
+ uint8 h = regAddress & 0xf0;
+ uint8 l = (regAddress & 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(regAddress, value);
break;
case 0x10:
- // adpcm
- warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ // pcm rhythm channel
+ if (_drv->_pcm)
+ _drv->_pcm->writeReg(regAddress - 0x10, value);
break;
case 0x20:
if (l == 8) {
@@ -1884,7 +2078,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 +2090,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 +2098,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,8 +2112,7 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
break;
case 0x90:
- // ssg
- warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAddress);
break;
case 0xa0:
@@ -1928,7 +2120,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) {
@@ -1964,13 +2156,13 @@ void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) {
break;
default:
- warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAddress);
break;
}
}
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 +2171,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
@@ -2033,7 +2225,7 @@ bool TownsPC98_OpnChannel::control_f4_setOutputLevel(uint8 para) {
}
bool TownsPC98_OpnChannel::control_f5_setTempo(uint8 para) {
- _drv->setTempo(para);
+ _drv->setMusicTempo(para);
return true;
}
@@ -2043,7 +2235,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 +2245,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;
@@ -2070,11 +2262,12 @@ bool TownsPC98_OpnChannel::control_f8_togglePitchWheel(uint8 para) {
_flags |= CHS_PITCHWHEELOFF;
}
} else {
- //uint8 skipChannels = para / 36;
- //uint8 entry = para % 36;
- //TownsPC98_OpnDriver::TownsPC98_OpnChannel *t = &chan[skipChannels];
- ////// NOT IMPLEMENTED
- //t->unnamedEntries[entry] = *_dataPtr++;
+ /* NOT IMPLEMENTED
+ uint8 skipChannels = para / 36;
+ uint8 entry = para % 36;
+ TownsPC98_OpnDriver::TownsPC98_OpnChannel *t = &chan[skipChannels];
+
+ t->unnamedEntries[entry] = *_dataPtr++;*/
}
return true;
}
@@ -2113,7 +2306,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 +2320,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 +2332,248 @@ 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;
+
+ 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 +2585,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 +2596,481 @@ 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->_finishedSfxFlag |= _idFlag;
+ _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;
+void TownsPC98_OpnChannelPCM::reset() {
+ TownsPC98_OpnChannel::reset();
+
+ if (_drv->_pcm)
+ _drv->_pcm->reset();
+}
+
+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 {
- keyOn();
+ // quit parsing for active channel
+ --_dataPtr;
+ _flags |= CHS_EOT;
+ _drv->_finishedPCMFlag |= _idFlag;
+ return false;
+ }
+}
- if (_protect == false || cmd != _frqBlockMSB)
- _flags |= CHS_RECALCFREQ;
-
- _protect = (para & 0x80) ? true : false;
- _frqBlockMSB = cmd;
+TownsPC98_OpnSquareSineSource::TownsPC98_OpnSquareSineSource(const uint32 timerbase) : _tlTable(0),
+ _tleTable(0), _regIndex(_reg), _updateRequest(-1), _tickLength(timerbase * 27), _ready(0) {
+ memset(_reg, 0, 16);
+ memset(_channels, 0, sizeof(Channel) * 3);
+ reset();
+}
+
+TownsPC98_OpnSquareSineSource::~TownsPC98_OpnSquareSineSource() {
+ delete [] _tlTable;
+ delete [] _tleTable;
+}
+
+void TownsPC98_OpnSquareSineSource::init(const int *rsTable, const int *rseTable) {
+ if (_ready) {
+ reset();
+ return;
}
- _ticksLeft = para & 0x7f;
+ delete [] _tlTable;
+ delete [] _tleTable;
+ _tlTable = new int32[16];
+ _tleTable = new int32[32];
+ float a, b, d;
+ d = 801.0f;
+
+ 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;
+
+ 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_SSG)) {
+ 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;
+ }
+ _ready = true;
+}
+
+void TownsPC98_OpnSquareSineSource::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::processFrequency() {
- if (_flags & CHS_RECALCFREQ) {
- uint8 block = (_frqBlockMSB & 0x70) >> 1;
- uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f];
- frequency = (bfreq + _frqLSB) | (block << 8);
+uint8 TownsPC98_OpnSquareSineSource::readReg(uint8 address) {
+ return _reg[address];
+}
- writeReg(_regOffset + 0xa4, (frequency >> 8));
- writeReg(_regOffset + 0xa0, (frequency & 0xff));
+void TownsPC98_OpnSquareSineSource::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_OpnSquareSineSource: event buffer overflow");
+ _updateRequest = -1;
+ }
+ _updateRequestBuf[++_updateRequest] = value;
+ _updateRequestBuf[++_updateRequest] = o;
+ return;
+ }
+
+ *_regIndex = value;
+}
+
+void TownsPC98_OpnSquareSineSource::nextTick(int32 *buffer, uint32 bufferSize) {
+ if (!_ready)
+ return;
+
+ for (uint32 i = 0; i < bufferSize; i++) {
+ _timer += _tickLength;
+ while (_timer > 0x5B8D80) {
+ _timer -= 0x5B8D80;
+
+ 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));
+ }
- _ptchWhlCurDelay = _ptchWhlInitDelayHi;
- if (_flags & CHS_KEYOFF) {
- _ptchWhlModCurVal = _ptchWhlModInitVal;
- _ptchWhlCurDelay += _ptchWhlInitDelayLo;
+ 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();
}
- _ptchWhlDurLeft = (_ptchWhlDuration >> 1);
- _flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
+ int32 finOut = 0;
+ for (int ii = 0; ii < 3; ii++) {
+ if ((_reg[ii + 8] >> 4) & 1)
+ finOut += _tleTable[_channels[ii].out ? _pReslt : 0];
+ else
+ finOut += _tlTable[_channels[ii].out ? (_reg[ii + 8] & 0x0f) : 0];
+ }
+
+ finOut /= 2;
+ buffer[i << 1] += finOut;
+ buffer[(i << 1) + 1] += finOut;
}
+}
- if (!(_flags & CHS_PITCHWHEELOFF)) {
- if (--_ptchWhlCurDelay)
- return;
- _ptchWhlCurDelay = _ptchWhlInitDelayHi;
- frequency += _ptchWhlModCurVal;
+void TownsPC98_OpnSquareSineSource::updatesRegs() {
+ for (int i = 0; i < _updateRequest;) {
+ uint8 b = _updateRequestBuf[i++];
+ uint8 a = _updateRequestBuf[i++];
+ writeReg(a, b, true);
+ }
+ _updateRequest = -1;
+}
- writeReg(_regOffset + 0xa4, (frequency >> 8));
- writeReg(_regOffset + 0xa0, (frequency & 0xff));
+TownsPC98_OpnPercussionSource::TownsPC98_OpnPercussionSource(const uint32 timerbase) :
+ _tickLength(timerbase * 2), _timer(0), _ready(false) {
+ memset(_pcmInstr, 0, sizeof(PcmInstrument) * 6);
+}
- if(!--_ptchWhlDurLeft) {
- _ptchWhlDurLeft = _ptchWhlDuration;
- _ptchWhlModCurVal = -_ptchWhlModCurVal;
+void TownsPC98_OpnPercussionSource::init(const uint8 *pcmData) {
+ if (_ready) {
+ reset();
+ return;
+ }
+
+ const uint8 *start = pcmData;
+ const uint8 *pos = start;
+
+ if (pcmData) {
+ for (int i = 0; i < 6; i++) {
+ _pcmInstr[i].data = start + READ_BE_UINT16(pos);
+ pos += 2;
+ _pcmInstr[i].size = READ_BE_UINT16(pos);
+ pos += 2;
}
+ reset();
+ _ready = true;
+ } else {
+ memset(_pcmInstr, 0, sizeof(PcmInstrument) * 6);
+ _ready = false;
}
}
-void TownsPC98_OpnChannelSSG::keyOff() {
- // all operators off
- uint8 value = _keyNum & 0x0f;
- uint8 regAdress = 0x28;
- writeReg(regAdress, value);
- _flags |= CHS_KEYOFF;
-}
+void TownsPC98_OpnPercussionSource::reset() {
+ _timer = 0;
+ _totalLevel = 63;
-void TownsPC98_OpnChannelSSG::keyOn() {
- // all operators on
- uint8 value = _keyNum | 0xf0;
- uint8 regAdress = 0x28;
- writeReg(regAdress, value);
+ memset(_regs, 0, 48);
+
+ for (int i = 0; i < 6; i++) {
+ PcmInstrument *s = &_pcmInstr[i];
+ s->pos = s->start = s->data;
+ s->end = s->data + s->size;
+ s->active = false;
+ s->level = 0;
+ s->out = 0;
+ s->decStep = 1;
+ s->decState = 0;
+ s->samples[0] = s->samples[1] = 0;
+ }
}
-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_OpnPercussionSource::writeReg(uint8 address, uint8 value) {
+ if (!_ready)
+ return;
+
+ uint8 h = address >> 4;
+ uint8 l = address & 15;
+
+ _regs[address] = value;
+
+ if (address == 0) {
+ if (value & 0x80) {
+ //key off
+ for (int i = 0; i < 6; i++) {
+ if ((value >> i) & 1)
+ _pcmInstr[i].active = false;
+ }
+ } else {
+ //key on
+ for (int i = 0; i < 6; i++) {
+ if ((value >> i) & 1) {
+ PcmInstrument *s = &_pcmInstr[i];
+ s->pos = s->start;
+ s->active = true;
+ s->out = 0;
+ s->samples[0] = s->samples[1] = 0;
+ s->decStep = 1;
+ s->decState = 0;
+ }
+ }
+ }
+ } else if (address == 1) {
+ // total level
+ _totalLevel = (value & 63) ^ 63;
+ for (int i = 0; i < 6; i++)
+ recalcOuput(&_pcmInstr[i]);
+ } else if (!h && l & 8) {
+ // instrument level
+ l &= 7;
+ _pcmInstr[l].level = (value & 0x1f) ^ 0x1f;
+ recalcOuput(&_pcmInstr[l]);
+ } else if (h & 3) {
+ l &= 7;
+ if (h == 1) {
+ // set start offset
+ _pcmInstr[l].start = _pcmInstr[l].data + ((_regs[24 + l] * 256 + _regs[16 + l]) << 8);
+ } else if (h == 2) {
+ // set end offset
+ _pcmInstr[l].end = _pcmInstr[l].data + ((_regs[40 + l] * 256 + _regs[32 + l]) << 8) + 255;
+ }
+ }
}
-void TownsPC98_OpnChannelSSG::opn_SSG_UNK(uint8 a) {
- _ssg1 = a;
- uint16 h = (_totalLevel + 1) * a;
- if ((h >> 8) == _ssg2)
+void TownsPC98_OpnPercussionSource::nextTick(int32 *buffer, uint32 bufferSize) {
+ if (!_ready)
return;
- _ssg2 = (h >> 8);
- writeReg(8 + _regOffset, _ssg2);
+
+ for (uint32 i = 0; i < bufferSize; i++) {
+ _timer += _tickLength;
+ while (_timer > 0x5B8D80) {
+ _timer -= 0x5B8D80;
+
+ for (int ii = 0; ii < 6; ii++) {
+ PcmInstrument *s = &_pcmInstr[ii];
+ if (s->active) {
+ recalcOuput(s);
+ if (s->decStep) {
+ advanceInput(s);
+ if (s->pos == s->end)
+ s->active = false;
+ }
+ s->decStep ^= 1;
+ }
+ }
+ }
+
+ int32 finOut = 0;
+
+ for (int ii = 0; ii < 6; ii++) {
+ if (_pcmInstr[ii].active)
+ finOut += _pcmInstr[ii].out;
+ }
+
+ finOut = (finOut * 7);
+
+ buffer[i << 1] += finOut;
+ buffer[(i << 1) + 1] += finOut;
+ }
+}
+
+void TownsPC98_OpnPercussionSource::recalcOuput(PcmInstrument *ins) {
+ uint32 s = _totalLevel + ins->level;
+ uint32 x = s > 62 ? 0 : (1 + (s >> 3));
+ int32 y = s > 62 ? 0 : (15 - (s & 7));
+ ins->out = ((ins->samples[ins->decStep] * y) >> x) & ~3;
+}
+
+void TownsPC98_OpnPercussionSource::advanceInput(PcmInstrument *ins) {
+ static const int8 adjustIndex[] = {-1, -1, -1, -1, 2, 5, 7, 9 };
+
+ static const int16 stepTable[] = { 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55,
+ 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337,
+ 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
+ };
+
+ uint8 cur = (int8) *ins->pos++;
+
+ for (int i = 0; i < 2; i++) {
+ int b = (2 * (cur & 7) + 1) * stepTable[ins->decState] / 8;
+ ins->samples[i] = CLIP<int16>(ins->samples[i ^ 1] + (cur & 8 ? b : -b), -2048, 2047);
+ ins->decState = CLIP<int8>(ins->decState + adjustIndex[cur & 7], 0, 48);
+ cur >>= 4;
+ }
}
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) {
- setTempo(84);
- _baserate = (486202500.0 / (double)getRate()) / 10368.0;
+ _mixer(mixer),
+
+ _channels(0), _ssgChannels(0), _sfxChannels(0), _pcmChannel(0), _ssg(0), _pcm(0),
+
+ _trackPtr(0), _sfxData(0), _sfxOffs(0), _ssgPatches(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),
+
+ _numChan(type == OD_TYPE26 ? 3 : 6), _numSSG(type == OD_TOWNS ? 0 : 3), _hasPCM(type == OD_TYPE86 ? true : false),
+ _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F), _finishedChannelsFlag(0),
+ _updateSSGFlag(type == OD_TOWNS ? 0x00 : 0x07), _finishedSSGFlag(0),
+ _updatePCMFlag(type == OD_TYPE86 ? 0x01 : 0x00), _finishedPCMFlag(0),
+ _updateSfxFlag(type == OD_TOWNS ? 0x00 : 0x06), _finishedSfxFlag(0),
+
+ _baserate(55125.0f / (float)getRate()),
+ _samplesTillMusicCallback(0), _samplesTillSfxCallback(0),
+ _samplesTillMusicCallbackRemainder(0), _samplesTillSfxCallbackRemainder(0),
+ _musicTickCounter(0),
+
+ _musicPlaying(false), _sfxPlaying(false), _fading(false), _looping(0), _ready(false) {
+
+ _timerbase = (uint32)(_baserate * 1000000.0f);
+ setMusicTempo(84);
+ setSfxTempo(654);
}
TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {
@@ -2381,13 +3088,26 @@ 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 _pcm;
+
delete [] _oprRates;
delete [] _oprRateshift;
delete [] _oprFrq;
delete [] _oprAttackDecay;
delete [] _oprSinTbl;
delete [] _oprLevelOut;
- delete [] _oprDetune;
+ delete [] _oprDetune;
+ delete [] _ssgPatches;
}
bool TownsPC98_OpnDriver::init() {
@@ -2420,7 +3140,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_OpnSquareSineSource(_timerbase);
+ _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 +3162,23 @@ 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) {
+ _pcm = new TownsPC98_OpnPercussionSource(_timerbase);
+ _pcm->init();
+
+ delete _pcmChannel;
+ _pcmChannel = new TownsPC98_OpnChannelPCM(this, 0, 0, 0, 0, 0, 1);
+ _pcmChannel->init();
}
_mixer->playInputStream(Audio::Mixer::kMusicSoundType,
@@ -2439,36 +3190,63 @@ bool TownsPC98_OpnDriver::init() {
int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples) {
memset(buffer, 0, sizeof(int16) * numSamples);
+ int32 *tmp = new int32[numSamples];
+ int32 *tmpStart = tmp;
+ memset(tmp, 0, sizeof(int32) * numSamples);
int32 samplesLeft = numSamples >> 1;
+
while (samplesLeft) {
- if (!_samplesTillCallback) {
- callback();
- _samplesTillCallback = _samplesPerCallback;
- _samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
- if (_samplesTillCallbackRemainder >= _tempo) {
- _samplesTillCallback++;
- _samplesTillCallbackRemainder -= _tempo;
+ if (!_samplesTillMusicCallback) {
+ musicCallback();
+ _samplesTillMusicCallback = _samplesPerMusicCallback;
+
+ _samplesTillMusicCallbackRemainder += _samplesPerMusicCallbackRemainder;
+ if (_samplesTillMusicCallbackRemainder >= _timerbase) {
+ _samplesTillMusicCallback++;
+ _samplesTillMusicCallbackRemainder -= _timerbase;
}
}
- int32 render = MIN(samplesLeft, _samplesTillCallback);
+ if (!_samplesTillSfxCallback) {
+ sfxCallback();
+ _samplesTillSfxCallback = _samplesPerSfxCallback;
+
+ _samplesTillSfxCallbackRemainder += _samplesPerSfxCallbackRemainder;
+ if (_samplesTillSfxCallbackRemainder >= _timerbase) {
+ _samplesTillSfxCallback++;
+ _samplesTillSfxCallbackRemainder -= _timerbase;
+ }
+ }
+
+ int32 render = MIN(samplesLeft, MIN(_samplesTillSfxCallback, _samplesTillMusicCallback));
samplesLeft -= render;
- _samplesTillCallback -= render;
- nextTick(buffer, render);
+ _samplesTillMusicCallback -= render;
+ _samplesTillSfxCallback -= render;
+
+ nextTick(tmp, render);
+
+ if (_ssg)
+ _ssg->nextTick(tmp, render);
+ if (_pcm)
+ _pcm->nextTick(tmp, render);
for (int i = 0; i < render; ++i) {
- buffer[i << 1] <<= 2;
- buffer[(i << 1) + 1] <<= 2;
+ int32 l = CLIP<int32>(tmp[i << 1], -32767, 32767);
+ buffer[i << 1] = (int16) l;
+ int32 r = CLIP<int32>(tmp[(i << 1) + 1], -32767, 32767);
+ buffer[(i << 1) + 1] = (int16) r;
}
buffer += (render << 1);
+ tmp += (render << 1);
}
+ delete [] tmpStart;
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 +3257,11 @@ void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) {
return;
}
+ reset();
+
lock();
- _trackData = data;
- reset();
-
- 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 +3278,179 @@ 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 = _finishedPCMFlag = 0;
+
+ _musicPlaying = (loadPaused ? false : true);
- // AH 0x17
unlock();
- _playing = (loadPaused ? false : true);
+}
+
+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;
+ }
+
+ if (!data) {
+ warning("TownsPC98_OpnDriver: Invalid sound effects 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;
+ _finishedSfxFlag = 0;
+ unlock();
}
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();
- _playing = _fading = false;
+ if (_ssg) {
+ for (int i = 0; i < 2; i++)
+ _sfxChannels[i]->reset();
+
+ memcpy(_ssgPatches, _drvTables + 276, 256);
+ _ssg->reset();
+ }
+
+ if (_pcmChannel)
+ _pcmChannel->reset();
+
+ _musicPlaying = false;
+ _sfxPlaying = false;
+ _fading = false;
_looping = 0;
- _tickCounter = 0;
+ _musicTickCounter = 0;
+ _sfxData = 0;
+
+ unlock();
}
-void TownsPC98_OpnDriver::fadeOut() {
- if (!_playing)
+void TownsPC98_OpnDriver::fadeStep() {
+ if (!_musicPlaying)
return;
- _fading = true;
+ lock();
- for (int i = 0; i < 20; i++) {
- lock();
- 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++)
+ for (int j = 0; j < _numChan; j++) {
+ if (_updateChannelsFlag & _channels[j]->_idFlag)
+ _channels[j]->fadeStep();
+ }
+
+ for (int j = 0; j < _numSSG; j++) {
+ if (_updateSSGFlag & _ssgChannels[j]->_idFlag)
_ssgChannels[j]->fadeStep();
+ }
- unlock();
-
- while (_playing) {
- if (_tickCounter >= dTime)
- break;
+ if (!_fading) {
+ _fading = 19;
+ if (_hasPCM) {
+ if (_updatePCMFlag & _pcmChannel->_idFlag)
+ _pcmChannel->reset();
}
+ } else {
+ if (!--_fading)
+ reset();
}
- _fading = false;
-
- reset();
+ unlock();
}
-void TownsPC98_OpnDriver::callback() {
- if (!_playing || --_cbCounter)
- return;
+void TownsPC98_OpnDriver::musicCallback() {
+ lock();
- _cbCounter = 4;
- _tickCounter++;
+ _sfxOffs = 0;
- lock();
+ if (_musicPlaying) {
+ _musicTickCounter++;
- for (int i = 0; i < _numChan; i++) {
- if (_updateChannelsFlag & _channels[i]->_idFlag) {
- _channels[i]->processEvents();
- _channels[i]->processFrequency();
+ 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++) {
- _ssgChannels[i]->processEvents();
- _ssgChannels[i]->processFrequency();
+ 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)
- return;
+void TownsPC98_OpnDriver::sfxCallback() {
+ lock();
- for (int i = 0; i < _numChan ; i++) {
- if (_channels[i]->_updateEnvelopes) {
- _channels[i]->_updateEnvelopes = false;
- _channels[i]->updateEnv();
+ if (_sfxChannels && _sfxPlaying) {
+ if (_sfxData)
+ startSoundEffect();
+
+ _sfxOffs = 3;
+ _trackPtr = _sfxBuffer;
+
+ for (int i = 0; i < 2; i++) {
+ if (_updateSfxFlag & _sfxChannels[i]->_idFlag) {
+ _sfxChannels[i]->processEvents();
+ _sfxChannels[i]->processFrequency();
+ }
}
-
- for (uint32 ii = 0; ii < bufferSize ; ii++)
- _channels[i]->generateOutput(buffer[ii * 2],
- buffer[ii * 2 + 1], &_channels[i]->_feedbuf[2], _channels[i]->_feedbuf);
+
+ _trackPtr = _musicBuffer;
}
- for (int i = 0; i < _numSSG ; i++) {
- if (_ssgChannels[i]->_updateEnvelopes) {
- _ssgChannels[i]->_updateEnvelopes = false;
- _ssgChannels[i]->updateEnv();
+ if (_finishedSfxFlag == _updateSfxFlag)
+ _sfxPlaying = false;
+
+ unlock();
+}
+
+void TownsPC98_OpnDriver::nextTick(int32 *buffer, uint32 bufferSize) {
+ if (!_ready)
+ return;
+
+ 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++)
- _ssgChannels[i]->generateOutput(buffer[ii * 2],
- buffer[ii * 2 + 1], &_ssgChannels[i]->_feedbuf[2], _ssgChannels[i]->_feedbuf);
+ _channels[i]->generateOutput(buffer[ii * 2],
+ buffer[ii * 2 + 1], &_channels[i]->_feedbuf[2], _channels[i]->_feedbuf);
}
}
@@ -2641,7 +3480,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,82 +3519,36 @@ 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::setTempo(uint8 tempo) {
- _tempo = tempo;
- _samplesPerCallback = getRate() / _tempo;
- _samplesPerCallbackRemainder = getRate() % _tempo;
-}
-
-const uint8 TownsPC98_OpnDriver::_drvTables[] = {
- // channel presets
- 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,
-
- // fmt level presets
- 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,
-
- // 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,
- 0x00, 0x00, 0x00, 0x00,
-
- // unused
- 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+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]);
+ }
+ }
- // detune
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
- 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
- 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
- 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
- 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
- 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a,
- 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14,
- 0x16, 0x16, 0x16, 0x16,
+ _sfxData = 0;
+}
- // 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
-};
+void TownsPC98_OpnDriver::setMusicTempo(uint8 tempo) {
+ float spc = (float)(0x100 - tempo) * 16.0f / _baserate;
+ _samplesPerMusicCallback = (int32) spc;
+ _samplesPerMusicCallbackRemainder = (uint32) ((spc - (float)_samplesPerMusicCallback) * 1000000.0f);
+}
-const uint32 TownsPC98_OpnDriver::_adtStat[] = {
- 0x00010001, 0x00010001, 0x00010001, 0x01010001,
- 0x00010101, 0x00010101, 0x00010101, 0x01010101,
- 0x01010101, 0x01010101, 0x01010102, 0x01010102,
- 0x01020102, 0x01020102, 0x01020202, 0x01020202,
- 0x02020202, 0x02020202, 0x02020204, 0x02020204,
- 0x02040204, 0x02040204, 0x02040404, 0x02040404,
- 0x04040404, 0x04040404, 0x04040408, 0x04040408,
- 0x04080408, 0x04080408, 0x04080808, 0x04080808,
- 0x08080808, 0x08080808, 0x10101010, 0x10101010
-};
+void TownsPC98_OpnDriver::setSfxTempo(uint16 tempo) {
+ float spc = (float)(0x400 - tempo) / _baserate;
+ _samplesPerSfxCallback = (int32) spc;
+ _samplesPerSfxCallbackRemainder = (uint32) ((spc - (float)_samplesPerSfxCallback) * 1000000.0f);
+}
SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
: Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _sfxFileData(0),
@@ -2916,7 +3709,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 +3789,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;
@@ -3050,18 +3844,18 @@ bool SoundPC98::init() {
void SoundPC98::playTrack(uint8 track) {
if (--track >= 56)
track -= 55;
-
+
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;
}
@@ -3074,29 +3868,42 @@ void SoundPC98::haltTrack() {
}
void SoundPC98::beginFadeOut() {
- _driver->fadeOut();
+ if (!_driver->musicPlaying())
+ return;
+
+ for (int i = 0; i < 20; i++) {
+ _driver->fadeStep();
+ _vm->delay(32);
+ }
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 +3917,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();
}
@@ -3138,9 +3951,9 @@ void SoundTownsPC98_v2::playTrack(uint8 track) {
char musicfile[13];
sprintf(musicfile, fileListEntry(0), 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);
@@ -3160,7 +3973,14 @@ void SoundTownsPC98_v2::haltTrack() {
}
void SoundTownsPC98_v2::beginFadeOut() {
- _driver->fadeOut();
+ if (!_driver->musicPlaying())
+ return;
+
+ for (int i = 0; i < 20; i++) {
+ _driver->fadeStep();
+ _vm->delay(32);
+ }
+
haltTrack();
}
@@ -3221,7 +4041,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,16 +4053,126 @@ 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);
+ _driver->loadSoundEffectData(_sfxTrackData, track);
+}
+
+// static resources
+
+const uint8 TownsPC98_OpnDriver::_drvTables[] = {
+ // channel presets
+ 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,
- //TODO
+ // fmt level presets
+ 0x54, 0x50, 0x4C, 0x48, 0x44, 0x40, 0x3C, 0x38,
+ 0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18,
+ 0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90,
- delete [] sd;
-}
+ // carriers
+ 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F,
+
+ // 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,
+ 0x00, 0x00, 0x00, 0x00,
+
+ // unused
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+
+ // detune
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
+ 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
+ 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14,
+ 0x16, 0x16, 0x16, 0x16,
+
+ // 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,
+
+ // 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[] = {
+ 0x00010001, 0x00010001, 0x00010001, 0x01010001,
+ 0x00010101, 0x00010101, 0x00010101, 0x01010101,
+ 0x01010101, 0x01010101, 0x01010102, 0x01010102,
+ 0x01020102, 0x01020102, 0x01020202, 0x01020202,
+ 0x02020202, 0x02020202, 0x02020204, 0x02020204,
+ 0x02040204, 0x02040204, 0x02040404, 0x02040404,
+ 0x04040404, 0x04040404, 0x04040408, 0x04040408,
+ 0x04080408, 0x04080408, 0x04080808, 0x04080808,
+ 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
+};
} // end of namespace Kyra