diff options
author | Florian Kagerer | 2008-08-16 12:38:37 +0000 |
---|---|---|
committer | Florian Kagerer | 2008-08-16 12:38:37 +0000 |
commit | 807bfe50796edd683d0aa026adc2cfbca7c490dd (patch) | |
tree | 8c47ffb8a7b2359412f74fcae6d4ab672b36dbfb /engines/kyra | |
parent | 49aff727e282edf3e07dcc20a8516ad60fdf7c11 (diff) | |
download | scummvm-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')
-rw-r--r-- | engines/kyra/kyra_hof.cpp | 12 | ||||
-rw-r--r-- | engines/kyra/sequences_hof.cpp | 3 | ||||
-rw-r--r-- | engines/kyra/sound.h | 5 | ||||
-rw-r--r-- | engines/kyra/sound_towns.cpp | 1320 |
4 files changed, 1021 insertions, 319 deletions
diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp index 33a8af91f1..0271f5ea0d 100644 --- a/engines/kyra/kyra_hof.cpp +++ b/engines/kyra/kyra_hof.cpp @@ -306,8 +306,10 @@ int KyraEngine_HoF::go() { _res->loadFileList(_ingamePakList, _ingamePakListSize); } - if (_flags.platform == Common::kPlatformPC98) + if (_flags.platform == Common::kPlatformPC98) { _res->loadPakFile("AUDIO.PAK"); + _sound->loadSoundFile("sound.dat"); + } } _menuDirectlyToLoad = (_menuChoice == 3) ? true : false; @@ -1578,10 +1580,14 @@ void KyraEngine_HoF::snd_playSoundEffect(int track, int volume) { int16 vocIndex = (int16)READ_LE_UINT16(&_ingameSoundIndex[track * 2]); if (vocIndex != -1) _sound->voicePlay(_ingameSoundList[vocIndex], true); - else if (_flags.platform != Common::kPlatformFMTowns) + else if (_flags.platform == Common::kPlatformPC) + KyraEngine_v1::snd_playSoundEffect(track); + // TODO ?? Maybe there is a way to let users select whether they want // voc, midi or adl sfx (even though it makes no sense to choose anything but voc). - KyraEngine_v1::snd_playSoundEffect(track); + // The PC-98 version has support for non-pcm sound effects, but only for tracks + // which also have voc files. The syntax would be: + // KyraEngine_v1::snd_playSoundEffect(vocIndex); } #pragma mark - diff --git a/engines/kyra/sequences_hof.cpp b/engines/kyra/sequences_hof.cpp index 635db3629c..3cd319fe77 100644 --- a/engines/kyra/sequences_hof.cpp +++ b/engines/kyra/sequences_hof.cpp @@ -2830,6 +2830,9 @@ void KyraEngine_HoF::seq_init() { _res->unloadAllPakFiles(); _res->loadPakFile(StaticResource::staticDataFilename()); _res->loadFileList(_sequencePakList, _sequencePakListSize); + + if (_flags.platform == Common::kPlatformPC98) + _sound->loadSoundFile("sound.dat"); int numShp = -1; diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h index e5294eb15d..2f7ac27ac5 100644 --- a/engines/kyra/sound.h +++ b/engines/kyra/sound.h @@ -429,7 +429,7 @@ public: MidiChannel *allocateChannel() { return 0; } MidiChannel *getPercussionChannel() { return 0; } - static float semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey, + static float calculatePhaseStep(int8 semiTone, int8 semiToneRootkey, uint32 sampleRate, uint32 outputRate, int32 pitchWheel); private: @@ -492,7 +492,7 @@ public: void process(); void loadSoundFile(uint file) {} - void loadSoundFile(Common::String) {} + void loadSoundFile(Common::String file); void playTrack(uint8 track); void haltTrack(); @@ -507,6 +507,7 @@ protected: bool _useFmSfx; uint8 *_musicTrackData; + uint8 *_sfxTrackData; TownsPC98_OpnDriver *_driver; }; 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 - |