diff options
author | athrxx | 2018-12-01 02:15:34 +0100 |
---|---|---|
committer | athrxx | 2019-03-07 21:28:41 +0100 |
commit | 49e85f64cf0470439de59ea87e342986b9779efc (patch) | |
tree | 297a6e277b36228f3280de683e25ad4ce570b0fb /audio/softsynth | |
parent | b789d50a55cc22e481d286a1038d3ec4092b23ec (diff) | |
download | scummvm-rg350-49e85f64cf0470439de59ea87e342986b9779efc.tar.gz scummvm-rg350-49e85f64cf0470439de59ea87e342986b9779efc.tar.bz2 scummvm-rg350-49e85f64cf0470439de59ea87e342986b9779efc.zip |
AUDIO: (FM-TOWNS/PC-98) - allow individual operator frequencies
(also add some sanity checks and make some more adjustments for SCI audio driver)
Diffstat (limited to 'audio/softsynth')
-rw-r--r-- | audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp | 160 | ||||
-rw-r--r-- | audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h | 2 |
2 files changed, 120 insertions, 42 deletions
diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp index e3d86b3515..ce006b165c 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp @@ -34,7 +34,8 @@ public: void keyOn(); void keyOff(); - void frequency(int freq); + void frequencyHi(uint8 frqH); + void frequencyLo(uint8 frqL); void updatePhaseIncrement(); void recalculateRates(); void generateOutput(int32 phasebuf, int32 *feedbuf, int32 &out); @@ -48,11 +49,14 @@ public: void sustainRate(uint32 value); void sustainLevel(uint32 value); void releaseRate(uint32 value); + void envelopeShape(uint32 value); void totalLevel(uint32 value); void ampModulation(bool enable); void reset(); protected: + void frequency(int freq); + EnvelopeState _state; bool _holdKey; uint32 _feedbackLevel; @@ -64,14 +68,18 @@ protected: uint32 _specifiedDecayRate; uint32 _specifiedSustainRate; uint32 _specifiedReleaseRate; + uint32 _envelopeShapeSpecs; uint32 _tickCount; uint32 _sustainLevel; bool _ampMod; uint32 _frequency; + uint16 _freqTemp; uint8 _kcode; uint32 _phase; uint32 _phaseIncrement; + uint32 _shapeState; + uint8 _shapeScale; const int32 *_detn; const uint8 *_rateTbl; @@ -98,9 +106,9 @@ TownsPC98_FmSynthOperator::TownsPC98_FmSynthOperator(const uint32 timerbase, con const uint32 *frqTable, const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) : _rtt(rtt), _rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable), _sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(timerbase * 2), - _specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0), - _sustainLevel(0), _phase(0), _state(kEnvReady), _holdKey(false), _timer(0), _keyScale1(0), - _keyScale2(0), _currentLevel(1023), _ampMod(false), _tickCount(0) { + _specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _envelopeShapeSpecs(0), _specifiedSustainRate(0), + _sustainLevel(0), _phase(0), _shapeState(0), _shapeScale(0), _state(kEnvReady), _holdKey(false), _timer(0), _keyScale1(0), + _keyScale2(0), _freqTemp(0), _currentLevel(1023), _ampMod(false), _tickCount(0), _phaseIncrement(0) { fs_a.rate = fs_a.shift = fs_d.rate = fs_d.shift = fs_s.rate = fs_s.shift = fs_r.rate = fs_r.shift = 0; @@ -114,6 +122,7 @@ void TownsPC98_FmSynthOperator::keyOn() { _holdKey = true; _state = kEnvAttacking; _phase = 0; + _shapeState = _envelopeShapeSpecs; } void TownsPC98_FmSynthOperator::keyOff() { @@ -125,6 +134,15 @@ void TownsPC98_FmSynthOperator::keyOff() { _state = kEnvReleasing; } +void TownsPC98_FmSynthOperator::frequencyHi(uint8 frqH) { + _freqTemp = (_freqTemp & 0xff) | ((frqH & 0x3F) << 8); +} + +void TownsPC98_FmSynthOperator::frequencyLo(uint8 frqL) { + _freqTemp = (_freqTemp & 0xff00) | frqL; + frequency(_freqTemp); +} + void TownsPC98_FmSynthOperator::frequency(int freq) { uint8 block = (freq >> 11); uint16 pos = (freq & 0x7ff); @@ -132,6 +150,7 @@ void TownsPC98_FmSynthOperator::frequency(int freq) { _kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6)); _frequency = _fTbl[pos << 1] >> (7 - block); + _freqTemp = 0; } void TownsPC98_FmSynthOperator::updatePhaseIncrement() { @@ -196,13 +215,13 @@ void TownsPC98_FmSynthOperator::generateOutput(int32 phasebuf, int32 *feed, int3 targetTime = (1 << fs_d.shift) - 1; nextState = kEnvSustaining; targetLevel = _sustainLevel; - levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)]; + levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)] << _shapeScale; break; case kEnvSustaining: targetTime = (1 << fs_s.shift) - 1; nextState = kEnvSustaining; - targetLevel = 1023; - levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)]; + targetLevel = _shapeScale ? 832 : 1023; + levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)] << _shapeScale; break; case kEnvReleasing: targetTime = (1 << fs_r.shift) - 1; @@ -219,13 +238,25 @@ void TownsPC98_FmSynthOperator::generateOutput(int32 phasebuf, int32 *feed, int3 if ((_state == kEnvAttacking && _currentLevel <= targetLevel) || (_state != kEnvAttacking && _currentLevel >= targetLevel)) { if (_state != kEnvDecaying) _currentLevel = targetLevel; + if (_state == kEnvSustaining && _shapeScale) { + _currentLevel += 191; + if (_shapeState & 1) { + if (!(_shapeState & 0x10)) + _shapeState |= 0x40; + } else { + nextState = kEnvAttacking; + _phase = 0; + _currentLevel = 511; + _shapeState &= ~0x40; + } + } _state = nextState; } } } - uint32 lvlout = _totalLevel + (uint32) _currentLevel; - + uint32 lvlout = _totalLevel + ((uint32) _currentLevel ^ (_state != kEnvReleasing ? ((_shapeScale * (_shapeState & 4)) >> 3) * 1023 : 0)); + _shapeState ^= (((_shapeState & 0x40) >> 2) | ((_shapeState & 2) << 1)); int32 outp = 0; int32 *i = &outp, *o = &outp; @@ -301,6 +332,11 @@ void TownsPC98_FmSynthOperator::releaseRate(uint32 value) { recalculateRates(); } +void TownsPC98_FmSynthOperator::envelopeShape(uint32 value) { + _envelopeShapeSpecs = value; + _shapeScale = (value & 8) >> 2; +} + void TownsPC98_FmSynthOperator::totalLevel(uint32 value) { _totalLevel = value << 3; } @@ -324,6 +360,7 @@ void TownsPC98_FmSynthOperator::reset() { decayRate(0); releaseRate(0); sustainRate(0); + envelopeShape(0); feedbackLevel(0); totalLevel(127); ampModulation(false); @@ -369,6 +406,7 @@ private: int _evpUpdateCnt; uint8 _outN; int _nTick; + uint8 _evpSwap; int32 *_tlTable; int32 *_tleTable; @@ -389,6 +427,9 @@ private: uint8 _noiseGenerator; uint8 _chanEnable; + uint8 _envH; + uint8 _envL; + uint8 _flags; uint8 **_reg; @@ -473,12 +514,12 @@ private: TownsPC98_FmSynthSquareSineSource::TownsPC98_FmSynthSquareSineSource(const uint32 timerbase, const uint32 rtt) : _tlTable(0), _rtt(rtt), _tleTable(0), _updateRequest(-1), _tickLength(timerbase * 27), _ready(0), _reg(0), _rand(1), _outN(1), _nTick(0), _evpUpdateCnt(0), _evpTimer(0x1f), _pReslt(0x1f), _attack(0), _cont(false), _evpUpdate(true), - _timer(0), _noiseGenerator(0), _chanEnable(0), _volumeT(0x60), + _timer(0), _noiseGenerator(0), _chanEnable(0), _envH(0), _envL(0), _flags(0), _evpSwap(0), _volumeT(0x60), _volMaskA(0), _volMaskB(0), _volumeA(Audio::Mixer::kMaxMixerVolume), _volumeB(Audio::Mixer::kMaxMixerVolume) { memset(_channels, 0, sizeof(_channels)); memset(_updateRequestBuf, 0, sizeof(_updateRequestBuf)); - _reg = new uint8 *[11]; + _reg = new uint8 *[14]; _reg[0] = &_channels[0].frqL; _reg[1] = &_channels[0].frqH; @@ -491,6 +532,9 @@ TownsPC98_FmSynthSquareSineSource::TownsPC98_FmSynthSquareSineSource(const uint3 _reg[8] = &_channels[0].vol; _reg[9] = &_channels[1].vol; _reg[10] = &_channels[2].vol; + _reg[11] = &_envL; + _reg[12] = &_envH; + _reg[13] = &_flags; reset(); } @@ -547,6 +591,7 @@ void TownsPC98_FmSynthSquareSineSource::reset() { _cont = false; _evpUpdate = true; _timer = 0; + _evpSwap = 0; for (int i = 0; i < 3; i++) { _channels[i].tick = 0; @@ -563,13 +608,23 @@ void TownsPC98_FmSynthSquareSineSource::writeReg(uint8 address, uint8 value, boo if (!_ready) return; - if (address > 10 || *_reg[address] == value) { - if ((address == 11 || address == 12 || address == 13) && value) - warning("TownsPC98_FmSynthSquareSineSource: unsupported reg address: %d", address); + if (address > 13) { + warning("TownsPC98_FmSynthSquareSineSource: unsupported reg address: %d", address); return; } if (!force) { + bool alreadyBuffered = false; + for (int i = 0; i < _updateRequest;) { + uint8 b = _updateRequestBuf[i++]; + uint8 a = _updateRequestBuf[i++]; + if (a == address) + alreadyBuffered = (b == value) ? true : false; + } + + if (alreadyBuffered) + return; + if (_updateRequest >= 63) { warning("TownsPC98_FmSynthSquareSineSource: event buffer overflow"); _updateRequest = -1; @@ -583,7 +638,7 @@ void TownsPC98_FmSynthSquareSineSource::writeReg(uint8 address, uint8 value, boo } uint8 TownsPC98_FmSynthSquareSineSource::readReg(uint8 address) const { - if (!_ready || address > 10) + if (!_ready || address > 13) return 0; return *_reg[address]; @@ -615,13 +670,15 @@ void TownsPC98_FmSynthSquareSineSource::nextTick(int32 *buffer, uint32 bufferSiz } if (_evpUpdate) { - if (++_evpUpdateCnt >= 0) { + if (++_evpUpdateCnt >= (_envH << 8 | _envL)) { _evpUpdateCnt = 0; if (--_evpTimer < 0) { if (_cont) { - _evpTimer &= 0x1f; + _attack ^= (_evpSwap && _evpTimer & 0x20) ? 0x1F : 0; + _evpTimer &= 0x1F; } else { + _attack ^= _evpSwap ? 0x1F : 0; _evpUpdate = false; _evpTimer = 0; } @@ -657,6 +714,15 @@ void TownsPC98_FmSynthSquareSineSource::updateRegs() { uint8 b = _updateRequestBuf[i++]; uint8 a = _updateRequestBuf[i++]; writeReg(a, b, true); + + if (a == 13) { + _attack = (_flags & 4) ? 0x1F : 0; + _cont = (_flags & 8) ? (_flags & 1) ^ 1: false; + _evpSwap = (_flags & 8) ? _flags & 2 : _attack; + _evpTimer = 0x1F; + _evpUpdate = true; + _pReslt = _evpTimer ^ _attack; + } } _updateRequest = -1; } @@ -959,7 +1025,6 @@ void TownsPC98_FmSynth::reset() { _chanInternal[i].opr[ii]->reset(); memset(_chanInternal[i].feedbuf, 0, 3 * sizeof(int32)); _chanInternal[i].algorithm = 0; - _chanInternal[i].frqTemp = 0; _chanInternal[i].enableLeft = _chanInternal[i].enableRight = true; _chanInternal[i].updateEnvelopeParameters = false; } @@ -982,9 +1047,9 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) { if (!_ready) return; - if (part > 1) { + if (part > (_numChan >> 2)) { warning("TownsPC98_FmSynth::writeReg(): invalid part argument '%d'", part); - part = 1; + return; } Common::StackLock lock(_mutex); @@ -1000,14 +1065,24 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) { TownsPC98_FmSynthOperator **co = 0; TownsPC98_FmSynthOperator *o = 0; - if (regAddress > 0x2F) { - c = &_chanInternal[(l & 3) + 3 * part]; - co = c->opr; - o = c->opr[oprOrdr[(l - (l & 3)) >> 2]]; - } else if (regAddress == 0x28) { + bool checkAddress = false; + c = &_chanInternal[(l & 3) + 3 * part]; + if (regAddress == 0x28) { c = &_chanInternal[(value & 3) + ((value & 4) ? 3 : 0)]; - co = c->opr; + checkAddress = true; + } else if (h == 0xA0 && (_registers[0x27][0] & 0x40) && (l > 7 || l == 2 || l == 6)) { + c = &_chanInternal[3 * part + 2]; + o = c->opr[l < 8 ? 3 : 2 - (l & 3)]; + } else if (h != 0xA0 && h > 0x20) { + o = c->opr[oprOrdr[(l & ~3) >> 2]]; + checkAddress = true; + } + + if (checkAddress && (l & 3) == 3) { + warning("TownsPC98_FmSynth::writeReg(): invalid write attempt at reg address 0x%2x", regAddress); + return; } + co = c->opr; switch (h) { case 0x00: @@ -1027,9 +1102,9 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) { // Key on/off for (int i = 0; i < 4; i++) { if ((value >> (4 + i)) & 1) - co[oprOrdr[i]]->keyOn(); + co[i]->keyOn(); else - co[oprOrdr[i]]->keyOff(); + co[i]->keyOff(); } } else if (l == 4) { // Timer A @@ -1082,6 +1157,7 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) { _timers[1].smpTillCb = _timers[1].smpPerCb; _timers[1].smpTillCbRem = _timers[1].smpPerCbRem; } + } else if (l == 2) { // LFO if (value & 8) @@ -1130,26 +1206,30 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) { break; case 0x90: - warning("TownsPC98_FmSynth: TRYING TO USE SSG ENVELOPE SHAPES (NOT SUPPORTED)"); + o->envelopeShape(value & 0x0f); break; case 0xa0: // frequency l &= ~3; if (l == 0) { - c->frqTemp = (c->frqTemp & 0xff00) | value; c->updateEnvelopeParameters = true; - c->fmIndex = (c->frqTemp >> 4 & 0x7f); - for (int i = 0; i < 4; i++) - co[i]->frequency(c->frqTemp); + if (o) + o->frequencyLo(value); + else + for (int i = 0; i < 4; i++) + co[i]->frequencyLo(value); } else if (l == 4) { - c->frqTemp = (c->frqTemp & 0xff) | (value << 8); - } else if (l == 8) { - // Ch 3/6 special mode frq - warning("TownsPC98_FmSynth: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)"); - } else if (l == 12) { - // Ch 3/6 special mode frq - warning("TownsPC98_FmSynth: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)"); + if (o) + o->frequencyHi(value); + else + for (int i = 0; i < 4; i++) + co[i]->frequencyHi(value); + } else if (l == 8 && o) { + c->updateEnvelopeParameters = true; + o->frequencyLo(value); + } else if (l == 12 && o) { + o->frequencyHi(value); } break; diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h index ba484e2413..58b7ccb230 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h @@ -116,8 +116,6 @@ private: frqModSvty = value << 5; } - uint16 frqTemp; - uint8 fmIndex; bool enableLeft; bool enableRight; bool updateEnvelopeParameters; |